| // Copyright 2018 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package owners |
| |
| import ( |
| "bytes" |
| "encoding/json" |
| "io" |
| "log" |
| "net/http" |
| "net/http/httptest" |
| "os" |
| "testing" |
| |
| "github.com/google/go-cmp/cmp" |
| ) |
| |
| func TestMatch(t *testing.T) { |
| testCases := []struct { |
| path string |
| entry *Entry |
| }{ |
| { |
| "sync", |
| &Entry{ |
| Primary: []Owner{bcmills}, |
| }, |
| }, |
| { |
| "crypto/chacha20poly1305/chacha20poly1305.go", |
| &Entry{ |
| Primary: []Owner{filippo, roland, securityTeam}, |
| }, |
| }, |
| { |
| "go/src/archive/zip/a.go", |
| &Entry{ |
| Primary: []Owner{joetsai}, |
| Secondary: []Owner{bradfitz}, |
| }, |
| }, |
| { |
| "go/src/cmd/compile", |
| &Entry{ |
| Primary: []Owner{compilerTeam}, |
| Secondary: []Owner{khr, gri, mdempsky, martisch}, |
| }, |
| }, |
| { |
| "go/path/with/no/owners", nil, |
| }, |
| { |
| "nonexistentrepo/foo/bar", nil, |
| }, |
| } |
| for _, tc := range testCases { |
| matches := match(tc.path) |
| if diff := cmp.Diff(matches, tc.entry); diff != "" { |
| t.Errorf("%s: owners differ (-got +want)\n%s", tc.path, diff) |
| } |
| } |
| } |
| |
| func TestHandler(t *testing.T) { |
| testCases := []struct { |
| method string |
| code int |
| paths []string |
| entries map[string]*Entry |
| }{ |
| {"PUT", http.StatusMethodNotAllowed, nil, nil}, |
| {"OPTIONS", http.StatusOK, nil, nil}, |
| { |
| "POST", http.StatusOK, |
| []string{"nonexistent/path"}, |
| map[string]*Entry{"nonexistent/path": nil}, |
| }, |
| { |
| "POST", http.StatusOK, |
| []string{"go/src/archive/zip/a.go"}, |
| map[string]*Entry{"go/src/archive/zip/a.go": {Primary: []Owner{joetsai}, Secondary: []Owner{bradfitz}}}, |
| }, |
| { |
| "POST", http.StatusOK, |
| []string{ |
| "go/src/archive/zip/a.go", |
| "go/src/archive/zip/b.go", |
| }, |
| map[string]*Entry{ |
| "go/src/archive/zip/a.go": {Primary: []Owner{joetsai}, Secondary: []Owner{bradfitz}}, |
| "go/src/archive/zip/b.go": {Primary: []Owner{joetsai}, Secondary: []Owner{bradfitz}}, |
| }, |
| }, |
| { |
| "POST", http.StatusOK, |
| []string{ |
| "go/src/archive/zip/a.go", |
| "crypto/chacha20poly1305/chacha20poly1305.go", |
| }, |
| map[string]*Entry{ |
| "go/src/archive/zip/a.go": {Primary: []Owner{joetsai}, Secondary: []Owner{bradfitz}}, |
| "crypto/chacha20poly1305/chacha20poly1305.go": {Primary: []Owner{filippo, roland, securityTeam}}, |
| }, |
| }, |
| } |
| |
| for _, tc := range testCases { |
| var buf bytes.Buffer |
| if tc.paths != nil { |
| var oReq Request |
| oReq.Payload.Paths = tc.paths |
| if err := json.NewEncoder(&buf).Encode(oReq); err != nil { |
| t.Errorf("could not encode request: %v", err) |
| continue |
| } |
| } |
| rStr := buf.String() |
| if rStr == "" { |
| rStr = "<empty>" |
| } |
| t.Logf("Request: %v", rStr) |
| req, err := http.NewRequest(tc.method, "/owners", &buf) |
| if err != nil { |
| t.Errorf("http.NewRequest: %v", err) |
| continue |
| } |
| w := httptest.NewRecorder() |
| Handler(w, req) |
| resp := w.Result() |
| if got, want := resp.StatusCode, tc.code; got != want { |
| t.Errorf("status code: got %v; want %v", got, want) |
| } |
| |
| if tc.code != http.StatusOK || tc.method == "OPTIONS" { |
| continue |
| } |
| var oResp Response |
| if err := json.NewDecoder(resp.Body).Decode(&oResp); err != nil { |
| t.Errorf("json decode: %v", err) |
| } |
| if oResp.Error != "" { |
| t.Errorf("got unexpected error in response: %q", oResp.Error) |
| } |
| if diff := cmp.Diff(oResp.Payload.Entries, tc.entries); diff != "" { |
| t.Errorf("%s: (-got +want)\n%s", tc.method, diff) |
| } |
| } |
| } |
| |
| func TestIndex(t *testing.T) { |
| req, err := http.NewRequest("GET", "/owners", nil) |
| if err != nil { |
| t.Fatalf("http.NewRequest: %v", err) |
| } |
| w := httptest.NewRecorder() |
| Handler(w, req) |
| resp := w.Result() |
| if got, want := resp.StatusCode, http.StatusOK; got != want { |
| t.Errorf("status code: got %v; want %v", got, want) |
| } |
| } |
| |
| func TestBadRequest(t *testing.T) { |
| defer func(old io.Writer) { log.SetOutput(old) }(os.Stderr /* TODO: use log.Writer() when Go 1.14 is out */) |
| var logBuf bytes.Buffer |
| log.SetOutput(&logBuf) |
| defer func() { |
| if t.Failed() { |
| t.Logf("Log output: %s", &logBuf) |
| } |
| }() |
| |
| req, err := http.NewRequest("POST", "/owners", bytes.NewBufferString("malformed json")) |
| if err != nil { |
| t.Fatalf("http.NewRequest: %v", err) |
| } |
| w := httptest.NewRecorder() |
| Handler(w, req) |
| resp := w.Result() |
| if got, want := resp.StatusCode, http.StatusBadRequest; got != want { |
| t.Errorf("status code: got %v; want %v", got, want) |
| } |
| if got, want := resp.Header.Get("Content-Type"), "application/json"; got != want { |
| t.Errorf("Content-Type: got %q; want %q", got, want) |
| } |
| var oResp Response |
| if err := json.NewDecoder(resp.Body).Decode(&oResp); err != nil { |
| t.Fatalf("could not decode response: %v", err) |
| } |
| if got, want := oResp.Error, "unable to decode request"; got != want { |
| t.Errorf("response error text: got %q; want %q", got, want) |
| } |
| } |