github.com/m-lab/locate@v0.17.6/handler/monitoring_test.go (about) 1 package handler 2 3 import ( 4 "encoding/json" 5 "net/http" 6 "net/http/httptest" 7 "strings" 8 "testing" 9 "time" 10 11 "gopkg.in/square/go-jose.v2/jwt" 12 13 "github.com/go-test/deep" 14 "github.com/m-lab/access/controller" 15 "github.com/m-lab/go/rtx" 16 v2 "github.com/m-lab/locate/api/v2" 17 "github.com/m-lab/locate/clientgeo" 18 "github.com/m-lab/locate/static" 19 prom "github.com/prometheus/client_golang/api/prometheus/v1" 20 ) 21 22 func TestClient_Monitoring(t *testing.T) { 23 tests := []struct { 24 name string 25 claim *jwt.Claims 26 signer Signer 27 locator LocatorV2 28 path string 29 wantTokenPrefix string 30 wantKey string 31 wantErr *v2.Error 32 }{ 33 { 34 name: "success-machine", 35 claim: &jwt.Claims{ 36 Issuer: static.IssuerMonitoring, 37 Subject: "mlab1-lga0t.mlab-oti.measurement-lab.org", 38 Audience: jwt.Audience{static.AudienceLocate}, 39 Expiry: jwt.NewNumericDate(time.Now().Add(time.Minute)), 40 }, 41 signer: &fakeSigner{}, 42 locator: &fakeLocatorV2{ 43 targets: []v2.Target{{Machine: "mlab1-lga0t.measurement-lab.org"}}, 44 }, 45 path: "ndt/ndt5", 46 wantKey: "wss://:3010/ndt_protocol", 47 // The fakeSigner generates synthetic access tokens based on the claim constructed by the handler. 48 // The audience (machine), the subject (monitoring), and issuer (locate). The suffix is the timestamp, which varies. 49 wantTokenPrefix: "mlab1-lga0t.mlab-oti.measurement-lab.org--monitoring--locate--", 50 }, 51 { 52 name: "error-no-claim", 53 claim: nil, 54 path: "ndt/ndt5", 55 wantErr: &v2.Error{ 56 Type: "claim", 57 Title: "Must provide access_token", 58 Status: http.StatusBadRequest, 59 }, 60 }, 61 { 62 name: "error-bad-subject", 63 claim: &jwt.Claims{ 64 Issuer: static.IssuerMonitoring, 65 Subject: "this-is-an-invalid-hostname", 66 Audience: jwt.Audience{static.AudienceLocate}, 67 Expiry: jwt.NewNumericDate(time.Now().Add(time.Minute)), 68 }, 69 path: "ndt/ndt5", 70 wantErr: &v2.Error{ 71 Type: "subject", 72 Title: "Subject must be specified", 73 Status: http.StatusBadRequest, 74 }, 75 }, 76 { 77 name: "error-invalid-service-path", 78 claim: &jwt.Claims{ 79 Issuer: static.IssuerMonitoring, 80 Subject: "mlab1-lga0t.mlab-oti.measurement-lab.org", 81 Audience: jwt.Audience{static.AudienceLocate}, 82 Expiry: jwt.NewNumericDate(time.Now().Add(time.Minute)), 83 }, 84 path: "ndt/this-is-an-invalid-service-name", 85 wantErr: &v2.Error{ 86 Type: "config", 87 Title: "Unknown service: ndt/this-is-an-invalid-service-name", 88 Status: http.StatusBadRequest, 89 }, 90 }, 91 } 92 for _, tt := range tests { 93 t.Run(tt.name, func(t *testing.T) { 94 cl := clientgeo.NewAppEngineLocator() 95 c := NewClient("mlab-sandbox", tt.signer, tt.locator, cl, prom.NewAPI(nil), nil, nil, nil) 96 rw := httptest.NewRecorder() 97 req := httptest.NewRequest(http.MethodGet, "/v2/platform/monitoring/"+tt.path, nil) 98 req = req.Clone(controller.SetClaim(req.Context(), tt.claim)) 99 100 c.Monitoring(rw, req) 101 102 q := v2.MonitoringResult{} 103 err := json.Unmarshal(rw.Body.Bytes(), &q) 104 rtx.Must(err, "Failed to unmarshal") 105 106 if tt.wantErr != nil { 107 if q.Error == nil { 108 t.Fatal("Monitoring() expected error, got nil") 109 } 110 if diff := deep.Equal(q.Error, tt.wantErr); diff != nil { 111 t.Errorf("Monitoring() expected error: got: %#v", diff) 112 } 113 return 114 } 115 if q.Target == nil { 116 t.Fatalf("Monitoring() returned nil Target") 117 } 118 if q.Target.Machine != tt.claim.Subject { 119 t.Errorf("Monitoring() returned different machine than claim subject; got %s, want %s", 120 q.Target.Machine, tt.claim.Subject) 121 } 122 if len(q.Target.URLs) != len(static.Configs[tt.path]) { 123 t.Errorf("Monitoring() returned incomplete urls; got %d, want %d", 124 len(q.Target.URLs), len(static.Configs[tt.path])) 125 } 126 if q.AccessToken == "" { 127 t.Errorf("Monitoring() expected AccessToken, got empty string") 128 } 129 if strings.Contains(tt.wantTokenPrefix, q.AccessToken) { 130 t.Errorf("Monitoring() did not get access token;\ngot %s,\nwant %s", q.AccessToken, tt.wantTokenPrefix) 131 } 132 if _, ok := q.Target.URLs[tt.wantKey]; !ok { 133 t.Errorf("Monitoring() result missing URLs key; want %q", tt.wantKey) 134 } 135 }) 136 } 137 }