package web
import (
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"time"
"git-browser/internal/repository"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
)
func TestServerRendersCorePages(t *testing.T) {
root := t.TempDir()
if err := initRepo(root, "demo"); err != nil {
t.Fatal(err)
}
store, err := repository.Discover(root)
if err != nil {
t.Fatal(err)
}
server, err := NewServer(store, Config{
CloneSSHPrefix: "git@localhost:repos/",
})
if err != nil {
t.Fatal(err)
}
repo, err := store.Open("demo")
if err != nil {
t.Fatal(err)
}
rev, err := repo.DefaultRevision()
if err != nil {
t.Fatal(err)
}
tests := []struct {
path string
want string
}{
{path: "/", want: "Repositories"},
{path: "/demo", want: "clone ssh: git@localhost:repos/demo"},
{path: "/demo", want: "revision: master"},
{path: "/demo", want: "README.md"},
{path: "/demo/blob/" + rev.Name + "/README.md", want: "aria-label=\"Breadcrumb\""},
{path: "/demo/blob/" + rev.Name + "/README.md", want: "/demo/raw/blob/" + rev.Name + "/README.md"},
{path: "/demo/log/" + rev.Name, want: "/demo/tree/"},
{path: "/demo/log/" + rev.Name + "?path=README.md", want: "back to file"},
{path: "/demo/branches", want: "feature"},
{path: "/demo/tree/feature/", want: "README.md"},
{path: "/demo/tree/feature/", want: "/demo/branches?rev=feature"},
}
for _, tc := range tests {
req := httptest.NewRequest(http.MethodGet, tc.path, nil)
req.Host = "localhost:8080"
rec := httptest.NewRecorder()
server.Handler().ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("%s: got status %d, want %d", tc.path, rec.Code, http.StatusOK)
}
if !strings.Contains(rec.Body.String(), tc.want) {
t.Fatalf("%s: response did not contain %q\nbody:\n%s", tc.path, tc.want, rec.Body.String())
}
}
}
func TestServerRendersConfiguredClonePrefixes(t *testing.T) {
root := t.TempDir()
if err := initRepo(root, "demo"); err != nil {
t.Fatal(err)
}
store, err := repository.Discover(root)
if err != nil {
t.Fatal(err)
}
server, err := NewServer(store, Config{
CloneSSHPrefix: "ssh://git.example.com/repos/",
CloneHTTPSPrefix: "https://git.example.com/repos/",
})
if err != nil {
t.Fatal(err)
}
req := httptest.NewRequest(http.MethodGet, "/demo", nil)
req.Host = "localhost:8080"
rec := httptest.NewRecorder()
server.Handler().ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("got status %d, want %d", rec.Code, http.StatusOK)
}
body := rec.Body.String()
for _, want := range []string{
"ssh://git.example.com/repos/demo",
"https://git.example.com/repos/demo",
"clone ssh:",
"clone https:",
} {
if !strings.Contains(body, want) {
t.Fatalf("response did not contain %q\nbody:\n%s", want, body)
}
}
}
func TestServerRefreshesRepositoryListBetweenRequests(t *testing.T) {
root := t.TempDir()
if err := initRepo(root, "alpha"); err != nil {
t.Fatal(err)
}
store, err := repository.Discover(root)
if err != nil {
t.Fatal(err)
}
server, err := NewServer(store, Config{
CloneSSHPrefix: "git@localhost:repos/",
})
if err != nil {
t.Fatal(err)
}
first := httptest.NewRequest(http.MethodGet, "/", nil)
first.Host = "localhost:8080"
firstRec := httptest.NewRecorder()
server.Handler().ServeHTTP(firstRec, first)
if firstRec.Code != http.StatusOK {
t.Fatalf("got status %d, want %d", firstRec.Code, http.StatusOK)
}
if strings.Contains(firstRec.Body.String(), "beta") {
t.Fatalf("unexpected repository in initial response\nbody:\n%s", firstRec.Body.String())
}
if err := initRepo(root, "beta"); err != nil {
t.Fatal(err)
}
waitForCondition(t, 2*time.Second, func() bool {
req := httptest.NewRequest(http.MethodGet, "/", nil)
req.Host = "localhost:8080"
rec := httptest.NewRecorder()
server.Handler().ServeHTTP(rec, req)
return rec.Code == http.StatusOK && strings.Contains(rec.Body.String(), "beta")
})
}
func TestServerServesRawBlob(t *testing.T) {
root := t.TempDir()
if err := initRepo(root, "demo"); err != nil {
t.Fatal(err)
}
store, err := repository.Discover(root)
if err != nil {
t.Fatal(err)
}
server, err := NewServer(store, Config{
CloneSSHPrefix: "git@localhost:repos/",
})
if err != nil {
t.Fatal(err)
}
repo, err := store.Open("demo")
if err != nil {
t.Fatal(err)
}
rev, err := repo.DefaultRevision()
if err != nil {
t.Fatal(err)
}
req := httptest.NewRequest(http.MethodGet, "/demo/raw/blob/"+rev.Name+"/README.md", nil)
req.Host = "localhost:8080"
rec := httptest.NewRecorder()
server.Handler().ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("got status %d, want %d", rec.Code, http.StatusOK)
}
if body := rec.Body.String(); body != "hello\n" {
t.Fatalf("got raw body %q, want %q", body, "hello\n")
}
if contentType := rec.Header().Get("Content-Type"); !strings.Contains(contentType, "text/plain") {
t.Fatalf("got content type %q, want text/plain", contentType)
}
}
func TestServerRendersEmptyRepositoryPage(t *testing.T) {
root := t.TempDir()
if _, err := git.PlainInit(filepath.Join(root, "empty"), false); err != nil {
t.Fatal(err)
}
store, err := repository.Discover(root)
if err != nil {
t.Fatal(err)
}
server, err := NewServer(store, Config{
CloneSSHPrefix: "",
})
if err != nil {
t.Fatal(err)
}
tests := []struct {
path string
want string
}{
{path: "/empty", want: "This repository has no commits yet."},
{path: "/empty/branches", want: "No branches found."},
}
for _, tc := range tests {
req := httptest.NewRequest(http.MethodGet, tc.path, nil)
req.Host = "localhost:8080"
rec := httptest.NewRecorder()
server.Handler().ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("%s: got status %d, want %d", tc.path, rec.Code, http.StatusOK)
}
if !strings.Contains(rec.Body.String(), tc.want) {
t.Fatalf("%s: response did not contain %q\nbody:\n%s", tc.path, tc.want, rec.Body.String())
}
if strings.Contains(rec.Body.String(), "clone ssh:") || strings.Contains(rec.Body.String(), "clone https:") {
t.Fatalf("%s: response unexpectedly contained clone URL block\nbody:\n%s", tc.path, rec.Body.String())
}
}
}
func TestServerOmitsCloneURLsWhenUnconfigured(t *testing.T) {
root := t.TempDir()
if err := initRepo(root, "demo"); err != nil {
t.Fatal(err)
}
store, err := repository.Discover(root)
if err != nil {
t.Fatal(err)
}
server, err := NewServer(store, Config{})
if err != nil {
t.Fatal(err)
}
req := httptest.NewRequest(http.MethodGet, "/demo", nil)
req.Host = "localhost:8080"
rec := httptest.NewRecorder()
server.Handler().ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("got status %d, want %d", rec.Code, http.StatusOK)
}
if strings.Contains(rec.Body.String(), "clone ssh:") || strings.Contains(rec.Body.String(), "clone https:") {
t.Fatalf("response unexpectedly contained clone URL block\nbody:\n%s", rec.Body.String())
}
}
func TestServerStripsDotGitSuffixOnlyInUI(t *testing.T) {
root := t.TempDir()
if err := initRepo(root, "demo.git"); err != nil {
t.Fatal(err)
}
store, err := repository.Discover(root)
if err != nil {
t.Fatal(err)
}
server, err := NewServer(store, Config{
CloneSSHPrefix: "git@localhost:repos/",
})
if err != nil {
t.Fatal(err)
}
tests := []struct {
path string
want []string
wantNot []string
}{
{
path: "/",
want: []string{">demo"},
wantNot: []string{">demo.git"},
},
{
path: "/demo.git",
want: []string{"