github.com/m-lab/locate@v0.17.6/handler/prometheus_test.go (about) 1 package handler 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "net/http" 8 "net/http/httptest" 9 "reflect" 10 "testing" 11 "time" 12 13 "github.com/m-lab/go/host" 14 "github.com/m-lab/go/testingx" 15 "github.com/m-lab/locate/connection/testdata" 16 "github.com/m-lab/locate/heartbeat" 17 "github.com/m-lab/locate/heartbeat/heartbeattest" 18 prom "github.com/prometheus/client_golang/api/prometheus/v1" 19 "github.com/prometheus/common/model" 20 ) 21 22 func TestClient_Prometheus(t *testing.T) { 23 tests := []struct { 24 name string 25 prom PrometheusClient 26 tracker heartbeat.StatusTracker 27 want int 28 }{ 29 { 30 name: "success", 31 prom: &fakePromClient{ 32 queryResult: model.Vector{}, 33 }, 34 tracker: &heartbeattest.FakeStatusTracker{}, 35 want: http.StatusOK, 36 }, 37 { 38 name: "e2e error", 39 prom: &fakePromClient{ 40 queryErr: e2eQuery, 41 queryResult: model.Vector{}, 42 }, 43 tracker: &heartbeattest.FakeStatusTracker{}, 44 want: http.StatusInternalServerError, 45 }, 46 { 47 name: "gmx error", 48 prom: &fakePromClient{ 49 queryErr: gmxQuery, 50 queryResult: model.Vector{}, 51 }, 52 tracker: &heartbeattest.FakeStatusTracker{}, 53 want: http.StatusInternalServerError, 54 }, 55 { 56 name: "tracker error", 57 prom: &fakePromClient{ 58 queryResult: model.Vector{}, 59 }, 60 tracker: &heartbeattest.FakeStatusTracker{ 61 Err: errors.New("error"), 62 }, 63 want: http.StatusInternalServerError, 64 }, 65 } 66 for _, tt := range tests { 67 t.Run(tt.name, func(t *testing.T) { 68 locator := heartbeat.NewServerLocator(tt.tracker) 69 locator.StopImport() 70 71 c := &Client{ 72 LocatorV2: locator, 73 PrometheusClient: tt.prom, 74 } 75 rw := httptest.NewRecorder() 76 req := httptest.NewRequest(http.MethodGet, "/v2/platform/prometheus", nil) 77 c.Prometheus(rw, req) 78 79 if tt.want != rw.Code { 80 t.Errorf("Prometheus() expected status code: %d, got: %d", tt.want, rw.Code) 81 } 82 }) 83 } 84 } 85 86 func TestClient_UpdatePrometheusForMachine(t *testing.T) { 87 hostname, err := host.Parse(testdata.FakeHostname) 88 testingx.Must(t, err, "failed to parse hostname") 89 90 tests := []struct { 91 name string 92 hostname string 93 prom PrometheusClient 94 tracker heartbeat.StatusTracker 95 wantErr bool 96 }{ 97 { 98 name: "success", 99 hostname: hostname.StringAll(), 100 prom: &fakePromClient{ 101 queryResult: model.Vector{}, 102 }, 103 tracker: &heartbeattest.FakeStatusTracker{}, 104 wantErr: false, 105 }, 106 { 107 name: "prom-error", 108 hostname: hostname.StringAll(), 109 prom: &fakePromClient{ 110 queryErr: formatQuery(e2eQuery, fmt.Sprintf("machine=%q", hostname.String())), 111 queryResult: model.Vector{}, 112 }, 113 tracker: &heartbeattest.FakeStatusTracker{}, 114 wantErr: true, 115 }, 116 { 117 name: "parse-error", 118 hostname: "invalid-hostname", 119 prom: &fakePromClient{ 120 queryResult: model.Vector{}, 121 }, 122 tracker: &heartbeattest.FakeStatusTracker{}, 123 wantErr: true, 124 }, 125 } 126 for _, tt := range tests { 127 t.Run(tt.name, func(t *testing.T) { 128 locator := heartbeat.NewServerLocator(tt.tracker) 129 locator.StopImport() 130 131 c := &Client{ 132 LocatorV2: locator, 133 PrometheusClient: tt.prom, 134 } 135 136 if err := c.UpdatePrometheusForMachine(context.Background(), tt.hostname); (err != nil) != tt.wantErr { 137 t.Errorf("Client.UpdatePrometheusForMachine() error = %v, wantErr %v", err, tt.wantErr) 138 } 139 }) 140 } 141 } 142 143 func TestClient_query(t *testing.T) { 144 tests := []struct { 145 name string 146 prom PrometheusClient 147 query string 148 label model.LabelName 149 f func(float64) bool 150 want map[string]bool 151 wantErr bool 152 }{ 153 { 154 name: "query-error", 155 prom: &fakePromClient{ 156 queryErr: "error", 157 }, 158 query: "error", 159 wantErr: true, 160 }, 161 { 162 name: "cast-error", 163 prom: &fakePromClient{ 164 queryResult: model.Matrix{}, 165 }, 166 query: "query", 167 wantErr: true, 168 }, 169 { 170 name: "e2e", 171 prom: &fakePromClient{ 172 queryResult: model.Vector{ 173 { 174 Metric: map[model.LabelName]model.LabelValue{ 175 e2eLabel: "success", 176 }, 177 Value: 1, 178 }, 179 { 180 Metric: map[model.LabelName]model.LabelValue{ 181 e2eLabel: "failure", 182 }, 183 Value: 0, 184 }, 185 }, 186 }, 187 query: e2eQuery, 188 label: e2eLabel, 189 f: e2eFunction, 190 want: map[string]bool{ 191 "success": true, 192 "failure": false, 193 }, 194 wantErr: false, 195 }, 196 { 197 name: "gmx", 198 prom: &fakePromClient{ 199 queryResult: model.Vector{ 200 { 201 Metric: map[model.LabelName]model.LabelValue{ 202 gmxLabel: "not-gmx", 203 }, 204 Value: 0, 205 }, 206 { 207 Metric: map[model.LabelName]model.LabelValue{ 208 gmxLabel: "gmx", 209 }, 210 Value: 1, 211 }, 212 }, 213 }, 214 query: gmxQuery, 215 label: gmxLabel, 216 f: gmxFunction, 217 want: map[string]bool{ 218 "not-gmx": true, 219 "gmx": false, 220 }, 221 wantErr: false, 222 }, 223 } 224 for _, tt := range tests { 225 t.Run(tt.name, func(t *testing.T) { 226 c := &Client{ 227 PrometheusClient: tt.prom, 228 } 229 230 got, err := c.query(context.Background(), tt.query, "", tt.label, tt.f) 231 if (err != nil) != tt.wantErr { 232 t.Errorf("Client.query() error = %v, wantErr %v", err, tt.wantErr) 233 return 234 } 235 236 if !reflect.DeepEqual(got, tt.want) { 237 t.Errorf("Client.query() = %v, want %v", got, tt.want) 238 } 239 }) 240 } 241 } 242 243 func Test_formatQuery(t *testing.T) { 244 tests := []struct { 245 name string 246 query string 247 filter string 248 want string 249 }{ 250 { 251 name: "filter", 252 query: "fake_metric", 253 filter: "machine=fake-machine-name", 254 want: "fake_metric{machine=fake-machine-name}", 255 }, 256 { 257 name: "no-filter", 258 query: "fake_metric", 259 filter: "", 260 want: "fake_metric", 261 }, 262 } 263 for _, tt := range tests { 264 t.Run(tt.name, func(t *testing.T) { 265 if got := formatQuery(tt.query, tt.filter); got != tt.want { 266 t.Errorf("formatQuery() = %v, want %v", got, tt.want) 267 } 268 }) 269 } 270 } 271 272 var errFakeQuery = errors.New("fake query error") 273 274 type fakePromClient struct { 275 queryErr string 276 queryResult model.Value 277 } 278 279 func (p *fakePromClient) Query(ctx context.Context, query string, ts time.Time, opts ...prom.Option) (model.Value, prom.Warnings, error) { 280 if query == p.queryErr { 281 return nil, prom.Warnings{}, errFakeQuery 282 } 283 284 return p.queryResult, prom.Warnings{}, nil 285 }