blob: 0533ed142d45f2919e7e6a28aba71ce033946de3 [file] [log] [blame]
// Copyright 2023 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 frontend
import (
"context"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
"time"
"github.com/google/safehtml/template"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/testing/fakedatasource"
"golang.org/x/pkgsite/static"
thirdparty "golang.org/x/pkgsite/third_party"
)
type testModule struct {
path string
redistributable bool
versions []string
packages []testPackage
}
type testPackage struct {
name string
suffix string
readmeContents string
readmeFilePath string
docs []*internal.Documentation
}
func newTestServer(t *testing.T, cacher Cacher) (*Server, http.Handler) {
t.Helper()
s, err := NewServer(ServerConfig{
DataSourceGetter: func(context.Context) internal.DataSource { return fakedatasource.New() },
TemplateFS: template.TrustedFSFromEmbed(static.FS),
// Use the embedded FSs here to make sure they're tested.
// Integration tests will use the actual directories.
StaticFS: static.FS,
ThirdPartyFS: thirdparty.FS,
})
if err != nil {
t.Fatal(err)
}
mux := http.NewServeMux()
s.Install(mux.Handle, cacher, nil)
return s, mux
}
func TestHTMLInjection(t *testing.T) {
_, handler := newTestServer(t, nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, httptest.NewRequest("GET", "/<em>UHOH</em>", nil))
if strings.Contains(w.Body.String(), "<em>") {
t.Error("User input was rendered unescaped.")
}
}
func mustRequest(urlPath string, t *testing.T) *http.Request {
t.Helper()
r, err := http.NewRequest(http.MethodGet, "http://localhost"+urlPath, nil)
if err != nil {
t.Fatal(err)
}
return r
}
func TestDetailsTTL(t *testing.T) {
tests := []struct {
r *http.Request
want time.Duration
}{
{mustRequest("/host.com/module@v1.2.3/suffix", t), longTTL},
{mustRequest("/host.com/module/suffix", t), shortTTL},
{mustRequest("/host.com/module@v1.2.3/suffix?tab=overview", t), longTTL},
{mustRequest("/host.com/module@v1.2.3/suffix?tab=versions", t), defaultTTL},
{mustRequest("/host.com/module@v1.2.3/suffix?tab=importedby", t), defaultTTL},
{
func() *http.Request {
r := mustRequest("/host.com/module@v1.2.3/suffix?tab=overview", t)
r.Header.Set("user-agent",
"Mozilla/5.0 (compatible; AhrefsBot/7.0; +http://ahrefs.com/robot/)")
return r
}(),
tinyTTL,
},
}
for _, test := range tests {
if got := detailsTTL(test.r); got != test.want {
t.Errorf("detailsTTL(%v) = %v, want %v", test.r, got, test.want)
}
}
}
func TestTagRoute(t *testing.T) {
mustRequest := func(url string) *http.Request {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
t.Fatal(err)
}
return req
}
tests := []struct {
route string
req *http.Request
want string
}{
{"/pkg", mustRequest("http://localhost/pkg/foo?tab=versions"), "pkg-versions"},
{"/", mustRequest("http://localhost/foo?tab=imports"), "imports"},
{"/search", mustRequest("http://localhost/search?q=net&m=vuln"), "search-vuln"},
{"/search", mustRequest("http://localhost/search?q=net&m=package"), "search-package"},
{"/search", mustRequest("http://localhost/search?q=net&m=symbol"), "search-symbol"},
{"/search", mustRequest("http://localhost/search?q=net"), "search-package"},
}
for _, test := range tests {
t.Run(test.want, func(t *testing.T) {
if got := TagRoute(test.route, test.req); got != test.want {
t.Errorf("TagRoute(%q, %v) = %q, want %q", test.route, test.req, got, test.want)
}
})
}
}
func TestInstallFS(t *testing.T) {
s, handler := newTestServer(t, nil)
s.InstallFS("/dir", os.DirFS("."))
// Request this file.
w := httptest.NewRecorder()
handler.ServeHTTP(w, httptest.NewRequest("GET", "/files/dir/frontend_test.go", nil))
if w.Code != http.StatusOK {
t.Errorf("got status code = %d, want %d", w.Code, http.StatusOK)
}
if want := "TestInstallFS"; !strings.Contains(w.Body.String(), want) {
t.Errorf("body does not contain %q", want)
}
}