| // 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. |
| |
| // Regression tests to run against a production instance of godoc. |
| |
| package main_test |
| |
| import ( |
| "bytes" |
| "flag" |
| "io" |
| "io/ioutil" |
| "net/http" |
| "net/url" |
| "regexp" |
| "strings" |
| "testing" |
| ) |
| |
| var host = flag.String("regtest.host", "", "host to run regression test against") |
| |
| func TestLiveServer(t *testing.T) { |
| *host = strings.TrimSuffix(*host, "/") |
| if *host == "" { |
| t.Skip("regtest.host flag missing.") |
| } |
| substringTests := []struct { |
| Message string |
| Path string |
| Substring string |
| Regexp string |
| NoAnalytics bool // expect the response to not contain GA. |
| PostBody string |
| StatusCode int // if 0, expect 2xx status code. |
| }{ |
| { |
| Path: "/doc/", |
| Substring: "an introduction to using modules in a simple project", |
| }, |
| { |
| Path: "/conduct", |
| Substring: "Project Stewards", |
| }, |
| { |
| Path: "/doc/faq", |
| Substring: "What is the purpose of the project", |
| }, |
| { |
| Path: "/pkg/", |
| Substring: "Package tar", |
| }, |
| { |
| Path: "/pkg/os/", |
| Substring: "func Open", |
| }, |
| { |
| Path: "/pkg/net/http/", |
| Substring: `title="Added in Go 1.11"`, |
| Message: "version information not present - failed InitVersionInfo?", |
| }, |
| { |
| Path: "/robots.txt", |
| Substring: "Disallow: /search", |
| Message: "robots not present - not deployed from Dockerfile?", |
| NoAnalytics: true, |
| }, |
| { |
| Path: "/change/75944e2e3a63", |
| Substring: "bdb10cf", |
| Message: "no change redirect - hg to git mapping not registered?", |
| NoAnalytics: true, |
| StatusCode: 302, |
| }, |
| { |
| Path: "/dl/", |
| Substring: `href="/dl/go1.11.windows-amd64.msi"`, |
| Message: "missing data on dl page - misconfiguration of datastore?", |
| }, |
| { |
| Path: "/dl/?mode=json", |
| Substring: ".windows-amd64.msi", |
| NoAnalytics: true, |
| }, |
| { |
| Message: "broken shortlinks - misconfiguration of datastore or memcache?", |
| Path: "/s/go2design", |
| Regexp: "proposal.*Found", |
| NoAnalytics: true, |
| StatusCode: 302, |
| }, |
| { |
| Message: "incorrect search result - broken index?", |
| Path: "/search?q=IsDir", |
| Substring: "src/os/types.go", |
| }, |
| { |
| Path: "/compile", |
| PostBody: "body=" + url.QueryEscape("package main; func main() { print(6*7); }"), |
| Regexp: `^{"compile_errors":"","output":"42"}$`, |
| NoAnalytics: true, |
| }, |
| { |
| Path: "/compile", |
| PostBody: "body=" + url.QueryEscape("//empty"), |
| Substring: "expected 'package', found 'EOF'", |
| NoAnalytics: true, |
| }, |
| { |
| Path: "/compile", |
| PostBody: "version=2&body=package+main%3Bimport+(%22fmt%22%3B%22time%22)%3Bfunc+main()%7Bfmt.Print(%22A%22)%3Btime.Sleep(time.Second)%3Bfmt.Print(%22B%22)%7D", |
| Regexp: `^{"Errors":"","Events":\[{"Message":"A","Kind":"stdout","Delay":0},{"Message":"B","Kind":"stdout","Delay":1000000000}\]}$`, |
| NoAnalytics: true, |
| }, |
| { |
| Path: "/share", |
| PostBody: "package main", |
| Substring: "", // just check it is a 2xx. |
| NoAnalytics: true, |
| }, |
| { |
| Path: "/x/net", |
| Substring: `<meta name="go-import" content="golang.org/x/net git https://go.googlesource.com/net">`, |
| NoAnalytics: true, |
| }, |
| { |
| Message: "release history page has an entry for Go 1.14.2", |
| Path: "/doc/devel/release.html", |
| Regexp: `go1\.14\.2\s+\(released 2020/04/08\)\s+includes\s+fixes to cgo, the go command, the runtime,`, |
| }, |
| { |
| Message: "Go project page has an entry for Go 1.14", |
| Path: "/project/", |
| Substring: `<li><a href="/doc/go1.14">Go 1.14</a> <small>(February 2020)</small></li>`, |
| }, |
| { |
| Message: "Go project subpath does not exist", |
| Path: "/project/notexist", |
| StatusCode: http.StatusNotFound, |
| }, |
| } |
| |
| for _, tc := range substringTests { |
| t.Run(tc.Path, func(t *testing.T) { |
| method := "GET" |
| var reqBody io.Reader |
| if tc.PostBody != "" { |
| method = "POST" |
| reqBody = strings.NewReader(tc.PostBody) |
| } |
| req, err := http.NewRequest(method, *host+tc.Path, reqBody) |
| if err != nil { |
| t.Fatalf("NewRequest: %v", err) |
| } |
| if reqBody != nil { |
| req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| } |
| resp, err := http.DefaultTransport.RoundTrip(req) |
| if err != nil { |
| t.Fatalf("RoundTrip: %v", err) |
| } |
| if tc.StatusCode == 0 { |
| if resp.StatusCode > 299 { |
| t.Errorf("Non-OK status code: %v", resp.StatusCode) |
| } |
| } else if tc.StatusCode != resp.StatusCode { |
| t.Errorf("StatusCode; got %v, want %v", resp.StatusCode, tc.StatusCode) |
| } |
| body, err := ioutil.ReadAll(resp.Body) |
| if err != nil { |
| t.Fatalf("ReadAll: %v", err) |
| } |
| |
| const googleAnalyticsID = "UA-11222381-2" // golang.org analytics ID |
| if !tc.NoAnalytics && !bytes.Contains(body, []byte(googleAnalyticsID)) { |
| t.Errorf("want response to contain analytics tracking ID") |
| } |
| |
| if tc.Substring != "" { |
| tc.Regexp = regexp.QuoteMeta(tc.Substring) |
| } |
| re := regexp.MustCompile(tc.Regexp) |
| |
| if !re.Match(body) { |
| t.Log("------ actual output -------") |
| t.Log(string(body)) |
| t.Log("----------------------------") |
| if tc.Message != "" { |
| t.Log(tc.Message) |
| } |
| t.Fatalf("wanted response to match %s", tc.Regexp) |
| } |
| }) |
| } |
| } |