...

Source file src/github.com/cybertec-postgresql/pgwatch/v3/internal/webserver/metric_test.go

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

     1  package webserver
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"io"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"testing"
    10  
    11  	"github.com/cybertec-postgresql/pgwatch/v3/internal/metrics"
    12  	"github.com/stretchr/testify/assert"
    13  )
    14  
    15  type mockMetricsReaderWriter struct {
    16  	GetMetricsFunc   func() (*metrics.Metrics, error)
    17  	UpdateMetricFunc func(name string, m metrics.Metric) error
    18  	DeleteMetricFunc func(name string) error
    19  	DeletePresetFunc func(name string) error
    20  	UpdatePresetFunc func(name string, preset metrics.Preset) error
    21  	WriteMetricsFunc func(metricDefs *metrics.Metrics) error
    22  }
    23  
    24  func (m *mockMetricsReaderWriter) GetMetrics() (*metrics.Metrics, error) {
    25  	return m.GetMetricsFunc()
    26  }
    27  func (m *mockMetricsReaderWriter) UpdateMetric(name string, metric metrics.Metric) error {
    28  	return m.UpdateMetricFunc(name, metric)
    29  }
    30  func (m *mockMetricsReaderWriter) DeleteMetric(name string) error {
    31  	return m.DeleteMetricFunc(name)
    32  }
    33  func (m *mockMetricsReaderWriter) DeletePreset(name string) error {
    34  	return m.DeletePresetFunc(name)
    35  }
    36  func (m *mockMetricsReaderWriter) UpdatePreset(name string, preset metrics.Preset) error {
    37  	return m.UpdatePresetFunc(name, preset)
    38  }
    39  func (m *mockMetricsReaderWriter) WriteMetrics(metricDefs *metrics.Metrics) error {
    40  	return m.WriteMetricsFunc(metricDefs)
    41  }
    42  
    43  func newTestMetricServer(mrw *mockMetricsReaderWriter) *WebUIServer {
    44  	return &WebUIServer{
    45  		metricsReaderWriter: mrw,
    46  	}
    47  }
    48  
    49  func TestHandleMetrics_GET(t *testing.T) {
    50  	mock := &mockMetricsReaderWriter{
    51  		GetMetricsFunc: func() (*metrics.Metrics, error) {
    52  			return &metrics.Metrics{MetricDefs: map[string]metrics.Metric{"foo": {Description: "foo"}}}, nil
    53  		},
    54  	}
    55  	ts := newTestMetricServer(mock)
    56  	r := httptest.NewRequest(http.MethodGet, "/metric", nil)
    57  	w := httptest.NewRecorder()
    58  	ts.handleMetrics(w, r)
    59  	resp := w.Result()
    60  	defer resp.Body.Close()
    61  	assert.Equal(t, http.StatusOK, resp.StatusCode)
    62  	body, _ := io.ReadAll(resp.Body)
    63  	var got map[string]metrics.Metric
    64  	assert.NoError(t, json.Unmarshal(body, &got))
    65  	assert.Contains(t, got, "foo")
    66  }
    67  
    68  func TestHandleMetrics_GET_Fail(t *testing.T) {
    69  	mock := &mockMetricsReaderWriter{
    70  		GetMetricsFunc: func() (*metrics.Metrics, error) {
    71  			return nil, errors.New("fail")
    72  		},
    73  	}
    74  	ts := newTestMetricServer(mock)
    75  	r := httptest.NewRequest(http.MethodGet, "/metric", nil)
    76  	w := httptest.NewRecorder()
    77  	ts.handleMetrics(w, r)
    78  	resp := w.Result()
    79  	defer resp.Body.Close()
    80  	assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
    81  	body, _ := io.ReadAll(resp.Body)
    82  	assert.Contains(t, string(body), "fail")
    83  }
    84  
    85  func TestHandleMetrics_POST(t *testing.T) {
    86  	var updatedName string
    87  	var updatedMetric metrics.Metric
    88  	mock := &mockMetricsReaderWriter{
    89  		UpdateMetricFunc: func(name string, m metrics.Metric) error {
    90  			updatedName = name
    91  			updatedMetric = m
    92  			return nil
    93  		},
    94  	}
    95  	ts := newTestMetricServer(mock)
    96  	m := metrics.Metric{Description: "bar"}
    97  	b, _ := json.Marshal(m)
    98  	r := httptest.NewRequest(http.MethodPost, "/metric?name=bar", bytes.NewReader(b))
    99  	w := httptest.NewRecorder()
   100  	ts.handleMetrics(w, r)
   101  	resp := w.Result()
   102  	defer resp.Body.Close()
   103  	assert.Equal(t, http.StatusOK, resp.StatusCode)
   104  	assert.Equal(t, "bar", updatedName)
   105  	assert.Equal(t, m, updatedMetric)
   106  }
   107  
   108  type errorReader struct{}
   109  
   110  func (e *errorReader) Read([]byte) (n int, err error) {
   111  	return 0, errors.New("mock read error")
   112  }
   113  
   114  func TestHandleMetrics_POST_ReaderFail(t *testing.T) {
   115  	mock := &mockMetricsReaderWriter{
   116  		UpdateMetricFunc: func(_ string, _ metrics.Metric) error {
   117  			return nil
   118  		},
   119  	}
   120  	ts := newTestMetricServer(mock)
   121  	r := httptest.NewRequest(http.MethodPost, "/metric?name=bar", &errorReader{})
   122  	w := httptest.NewRecorder()
   123  	ts.handleMetrics(w, r)
   124  	resp := w.Result()
   125  	defer resp.Body.Close()
   126  	assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
   127  	body, _ := io.ReadAll(resp.Body)
   128  	assert.Contains(t, string(body), "mock read error")
   129  }
   130  
   131  func TestHandleMetrics_POST_Fail(t *testing.T) {
   132  	mock := &mockMetricsReaderWriter{
   133  		UpdateMetricFunc: func(_ string, _ metrics.Metric) error {
   134  			return errors.New("fail")
   135  		},
   136  	}
   137  	ts := newTestMetricServer(mock)
   138  	m := metrics.Metric{Description: "bar"}
   139  	b, _ := json.Marshal(m)
   140  	r := httptest.NewRequest(http.MethodPost, "/metric?name=bar", bytes.NewReader(b))
   141  	w := httptest.NewRecorder()
   142  	ts.handleMetrics(w, r)
   143  	resp := w.Result()
   144  	defer resp.Body.Close()
   145  	assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
   146  	body, _ := io.ReadAll(resp.Body)
   147  	assert.Contains(t, string(body), "fail")
   148  }
   149  
   150  func TestHandleMetrics_DELETE(t *testing.T) {
   151  	var deletedName string
   152  	mock := &mockMetricsReaderWriter{
   153  		DeleteMetricFunc: func(name string) error {
   154  			deletedName = name
   155  			return nil
   156  		},
   157  	}
   158  	ts := newTestMetricServer(mock)
   159  	r := httptest.NewRequest(http.MethodDelete, "/metric?name=foo", nil)
   160  	w := httptest.NewRecorder()
   161  	ts.handleMetrics(w, r)
   162  	resp := w.Result()
   163  	defer resp.Body.Close()
   164  	assert.Equal(t, http.StatusOK, resp.StatusCode)
   165  	assert.Equal(t, "foo", deletedName)
   166  }
   167  
   168  func TestHandleMetrics_Options(t *testing.T) {
   169  	mock := &mockMetricsReaderWriter{}
   170  	ts := newTestMetricServer(mock)
   171  	r := httptest.NewRequest(http.MethodOptions, "/metric", nil)
   172  	w := httptest.NewRecorder()
   173  	ts.handleMetrics(w, r)
   174  	resp := w.Result()
   175  	defer resp.Body.Close()
   176  	assert.Equal(t, http.StatusNoContent, resp.StatusCode)
   177  	assert.Equal(t, "GET, POST, DELETE, OPTIONS", resp.Header.Get("Allow"))
   178  }
   179  
   180  func TestHandleMetrics_MethodNotAllowed(t *testing.T) {
   181  	mock := &mockMetricsReaderWriter{}
   182  	ts := newTestMetricServer(mock)
   183  	r := httptest.NewRequest(http.MethodPut, "/metric", nil)
   184  	w := httptest.NewRecorder()
   185  	ts.handleMetrics(w, r)
   186  	resp := w.Result()
   187  	defer resp.Body.Close()
   188  	assert.Equal(t, http.StatusMethodNotAllowed, resp.StatusCode)
   189  	assert.Equal(t, "GET, POST, DELETE, OPTIONS", resp.Header.Get("Allow"))
   190  }
   191  
   192  func TestGetMetrics_Error(t *testing.T) {
   193  	mock := &mockMetricsReaderWriter{
   194  		GetMetricsFunc: func() (*metrics.Metrics, error) {
   195  			return nil, errors.New("fail")
   196  		},
   197  	}
   198  	ts := newTestMetricServer(mock)
   199  	_, err := ts.GetMetrics()
   200  	assert.Error(t, err)
   201  }
   202  
   203  func TestUpdateMetric_Error(t *testing.T) {
   204  	mock := &mockMetricsReaderWriter{
   205  		UpdateMetricFunc: func(_ string, _ metrics.Metric) error {
   206  			return errors.New("fail")
   207  		},
   208  	}
   209  	ts := newTestMetricServer(mock)
   210  	err := ts.UpdateMetric("foo", []byte("notjson"))
   211  	assert.Error(t, err)
   212  }
   213  
   214  func TestDeleteMetric_Error(t *testing.T) {
   215  	mock := &mockMetricsReaderWriter{
   216  		DeleteMetricFunc: func(_ string) error {
   217  			return errors.New("fail")
   218  		},
   219  	}
   220  	ts := newTestMetricServer(mock)
   221  	err := ts.DeleteMetric("foo")
   222  	assert.Error(t, err)
   223  }
   224  
   225  func TestHandlePreset_GET(t *testing.T) {
   226  	mock := &mockMetricsReaderWriter{
   227  		GetMetricsFunc: func() (*metrics.Metrics, error) {
   228  			return &metrics.Metrics{PresetDefs: map[string]metrics.Preset{"foo": {Description: "foo"}}}, nil
   229  		},
   230  	}
   231  	ts := newTestMetricServer(mock)
   232  	r := httptest.NewRequest(http.MethodGet, "/preset", nil)
   233  	w := httptest.NewRecorder()
   234  	ts.handlePresets(w, r)
   235  	resp := w.Result()
   236  	defer resp.Body.Close()
   237  	assert.Equal(t, http.StatusOK, resp.StatusCode)
   238  	body, _ := io.ReadAll(resp.Body)
   239  	var got map[string]metrics.Preset
   240  	assert.NoError(t, json.Unmarshal(body, &got))
   241  	assert.Contains(t, got, "foo")
   242  }
   243  
   244  func TestHandlePreset_GET_Fail(t *testing.T) {
   245  	mock := &mockMetricsReaderWriter{
   246  		GetMetricsFunc: func() (*metrics.Metrics, error) {
   247  			return nil, errors.New("fail")
   248  		},
   249  	}
   250  	ts := newTestMetricServer(mock)
   251  	r := httptest.NewRequest(http.MethodGet, "/preset", nil)
   252  	w := httptest.NewRecorder()
   253  	ts.handlePresets(w, r)
   254  	resp := w.Result()
   255  	defer resp.Body.Close()
   256  	assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
   257  	body, _ := io.ReadAll(resp.Body)
   258  	assert.Contains(t, string(body), "fail")
   259  }
   260  
   261  func TestHandlePreset_POST(t *testing.T) {
   262  	var updatedName string
   263  	var updatedPreset metrics.Preset
   264  	mock := &mockMetricsReaderWriter{
   265  		UpdatePresetFunc: func(name string, p metrics.Preset) error {
   266  			updatedName = name
   267  			updatedPreset = p
   268  			return nil
   269  		},
   270  	}
   271  	ts := newTestMetricServer(mock)
   272  	p := metrics.Preset{Description: "bar"}
   273  	b, _ := json.Marshal(p)
   274  	r := httptest.NewRequest(http.MethodPost, "/preset?name=bar", bytes.NewReader(b))
   275  	w := httptest.NewRecorder()
   276  	ts.handlePresets(w, r)
   277  	resp := w.Result()
   278  	defer resp.Body.Close()
   279  	assert.Equal(t, http.StatusOK, resp.StatusCode)
   280  	assert.Equal(t, "bar", updatedName)
   281  	assert.Equal(t, p, updatedPreset)
   282  }
   283  
   284  func TestHandlePreset_POST_ReaderFail(t *testing.T) {
   285  	mock := &mockMetricsReaderWriter{
   286  		UpdatePresetFunc: func(string, metrics.Preset) error {
   287  			return nil
   288  		},
   289  	}
   290  	ts := newTestMetricServer(mock)
   291  	r := httptest.NewRequest(http.MethodPost, "/preset?name=bar", &errorReader{})
   292  	w := httptest.NewRecorder()
   293  	ts.handlePresets(w, r)
   294  	resp := w.Result()
   295  	defer resp.Body.Close()
   296  	assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
   297  	body, _ := io.ReadAll(resp.Body)
   298  	assert.Contains(t, string(body), "mock read error")
   299  }
   300  
   301  func TestHandlePreset_POST_Fail(t *testing.T) {
   302  	mock := &mockMetricsReaderWriter{
   303  		UpdatePresetFunc: func(string, metrics.Preset) error {
   304  			return errors.New("fail")
   305  		},
   306  	}
   307  	ts := newTestMetricServer(mock)
   308  	p := metrics.Preset{Description: "bar"}
   309  	b, _ := json.Marshal(p)
   310  	r := httptest.NewRequest(http.MethodPost, "/preset?name=bar", bytes.NewReader(b))
   311  	w := httptest.NewRecorder()
   312  	ts.handlePresets(w, r)
   313  	resp := w.Result()
   314  	defer resp.Body.Close()
   315  	assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
   316  	body, _ := io.ReadAll(resp.Body)
   317  	assert.Contains(t, string(body), "fail")
   318  }
   319  
   320  func TestHandlePreset_DELETE(t *testing.T) {
   321  	var deletedName string
   322  	mock := &mockMetricsReaderWriter{
   323  		DeletePresetFunc: func(name string) error {
   324  			deletedName = name
   325  			return nil
   326  		},
   327  	}
   328  	ts := newTestMetricServer(mock)
   329  	r := httptest.NewRequest(http.MethodDelete, "/preset?name=foo", nil)
   330  	w := httptest.NewRecorder()
   331  	ts.handlePresets(w, r)
   332  	resp := w.Result()
   333  	defer resp.Body.Close()
   334  	assert.Equal(t, http.StatusOK, resp.StatusCode)
   335  	assert.Equal(t, "foo", deletedName)
   336  }
   337  
   338  func TestHandlePreset_Options(t *testing.T) {
   339  	mock := &mockMetricsReaderWriter{}
   340  	ts := newTestMetricServer(mock)
   341  	r := httptest.NewRequest(http.MethodOptions, "/preset", nil)
   342  	w := httptest.NewRecorder()
   343  	ts.handlePresets(w, r)
   344  	resp := w.Result()
   345  	defer resp.Body.Close()
   346  	assert.Equal(t, http.StatusNoContent, resp.StatusCode)
   347  	assert.Equal(t, "GET, POST, PATCH, DELETE, OPTIONS", resp.Header.Get("Allow"))
   348  }
   349  
   350  func TestHandlePreset_MethodNotAllowed(t *testing.T) {
   351  	mock := &mockMetricsReaderWriter{}
   352  	ts := newTestMetricServer(mock)
   353  	r := httptest.NewRequest(http.MethodPut, "/preset", nil)
   354  	w := httptest.NewRecorder()
   355  	ts.handlePresets(w, r)
   356  	resp := w.Result()
   357  	defer resp.Body.Close()
   358  	assert.Equal(t, http.StatusMethodNotAllowed, resp.StatusCode)
   359  	assert.Equal(t, "GET, POST, PATCH, DELETE, OPTIONS", resp.Header.Get("Allow"))
   360  }
   361  
   362  func TestGetPresets_Error(t *testing.T) {
   363  	mock := &mockMetricsReaderWriter{
   364  		GetMetricsFunc: func() (*metrics.Metrics, error) {
   365  			return nil, errors.New("fail")
   366  		},
   367  	}
   368  	ts := newTestMetricServer(mock)
   369  	_, err := ts.GetPresets()
   370  	assert.Error(t, err)
   371  }
   372  
   373  func TestUpdatePreset_Error(t *testing.T) {
   374  	mock := &mockMetricsReaderWriter{
   375  		UpdatePresetFunc: func(string, metrics.Preset) error {
   376  			return errors.New("fail")
   377  		},
   378  	}
   379  	ts := newTestMetricServer(mock)
   380  	err := ts.UpdatePreset("foo", []byte("notjson"))
   381  	assert.Error(t, err)
   382  }
   383  
   384  func TestDeletePreset_Error(t *testing.T) {
   385  	mock := &mockMetricsReaderWriter{
   386  		DeletePresetFunc: func(string) error {
   387  			return errors.New("fail")
   388  		},
   389  	}
   390  	ts := newTestMetricServer(mock)
   391  	err := ts.DeletePreset("foo")
   392  	assert.Error(t, err)
   393  }
   394