145 lines
6.0 KiB
Go
145 lines
6.0 KiB
Go
package app
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/DATA-DOG/go-sqlmock"
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
func csrfFromResponse(t *testing.T, rr *httptest.ResponseRecorder) *http.Cookie {
|
|
t.Helper()
|
|
for _, c := range rr.Result().Cookies() {
|
|
if c.Name == webCSRFCookie {
|
|
return c
|
|
}
|
|
}
|
|
t.Fatalf("missing csrf cookie in %#v", rr.Result().Cookies())
|
|
return nil
|
|
}
|
|
|
|
func TestWebReposAndLogin(t *testing.T) {
|
|
s, mock, cleanup := newMockServer(t)
|
|
defer cleanup()
|
|
repo := Repository{ID: 10, OwnerUserID: 1, Owner: "alice", Name: "demo", Visibility: "public", Description: "desc", DefaultBranch: "main"}
|
|
mock.ExpectQuery("FROM repositories r JOIN users u").WithArgs("demo", "%demo%", "%demo%").WillReturnRows(repoRows(repo))
|
|
rr := httptest.NewRecorder()
|
|
s.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/repos?q=demo", nil))
|
|
if rr.Code != http.StatusOK || !strings.Contains(rr.Body.String(), "alice/demo") {
|
|
t.Fatalf("repos status=%d body=%s", rr.Code, rr.Body.String())
|
|
}
|
|
csrf := csrfFromResponse(t, rr)
|
|
|
|
rr = httptest.NewRecorder()
|
|
s.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/login", nil))
|
|
if rr.Code != http.StatusOK || !strings.Contains(rr.Body.String(), "<h1>Login</h1>") {
|
|
t.Fatalf("login GET status=%d body=%s", rr.Code, rr.Body.String())
|
|
}
|
|
|
|
hash, err := bcrypt.GenerateFromPassword([]byte("password123"), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
mock.ExpectQuery("SELECT id, email, username, is_admin, password_hash FROM users").WithArgs("alice", "alice").WillReturnRows(sqlmock.NewRows([]string{"id", "email", "username", "is_admin", "password_hash"}).AddRow(int64(1), "alice@example.com", "alice", false, string(hash)))
|
|
mock.ExpectExec("INSERT INTO auth_tokens").WithArgs(int64(1), sqlmock.AnyArg(), sqlmock.AnyArg()).WillReturnResult(sqlmock.NewResult(1, 1))
|
|
rr = httptest.NewRecorder()
|
|
req := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader("_csrf="+csrf.Value+"&login=alice&password=password123"))
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
req.AddCookie(csrf)
|
|
s.ServeHTTP(rr, req)
|
|
if rr.Code != http.StatusSeeOther || rr.Header().Get("Location") != "/repos" {
|
|
t.Fatalf("login POST status=%d location=%q body=%s", rr.Code, rr.Header().Get("Location"), rr.Body.String())
|
|
}
|
|
foundAuth := false
|
|
for _, c := range rr.Result().Cookies() {
|
|
if c.Name == webAuthCookie && c.Value != "" && c.HttpOnly {
|
|
foundAuth = true
|
|
}
|
|
}
|
|
if !foundAuth {
|
|
t.Fatalf("missing auth cookie: %#v", rr.Result().Cookies())
|
|
}
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestWebRepoPagesCreateEditAndForkRules(t *testing.T) {
|
|
requireGitForApp(t)
|
|
s, mock, cleanup := newMockServer(t)
|
|
defer cleanup()
|
|
user := User{ID: 1, Email: "alice@example.com", Username: "alice"}
|
|
repo := Repository{ID: 10, OwnerUserID: user.ID, Owner: "alice", Name: "demo", Visibility: "public", Description: "desc", DefaultBranch: "main", CreatedAt: time.Now(), UpdatedAt: time.Now()}
|
|
bare := s.repoPath(repo.Owner, repo.Name)
|
|
if err := os.MkdirAll(filepath.Dir(bare), 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
seedRepoWithFeatureBranch(t, bare)
|
|
|
|
expectLoadRepo(mock, "alice", "demo", repo)
|
|
rr := httptest.NewRecorder()
|
|
s.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/alice/demo", nil))
|
|
if rr.Code != http.StatusOK || !strings.Contains(rr.Body.String(), "README.md") || !strings.Contains(rr.Body.String(), "Clone:") {
|
|
t.Fatalf("repo page status=%d body=%s", rr.Code, rr.Body.String())
|
|
}
|
|
csrf := csrfFromResponse(t, rr)
|
|
|
|
expectLoadRepo(mock, "alice", "demo", repo)
|
|
rr = httptest.NewRecorder()
|
|
s.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/alice/demo/blob?ref=main&path=README.md", nil))
|
|
if rr.Code != http.StatusOK || !strings.Contains(rr.Body.String(), "hello") {
|
|
t.Fatalf("blob page status=%d body=%s", rr.Code, rr.Body.String())
|
|
}
|
|
|
|
expectBearerUser(mock, "tok", user)
|
|
mock.ExpectExec("INSERT INTO repositories").WithArgs(user.ID, "newrepo", "public", "new desc").WillReturnResult(sqlmock.NewResult(20, 1))
|
|
rr = httptest.NewRecorder()
|
|
req := httptest.NewRequest(http.MethodPost, "/repos/new", strings.NewReader("_csrf="+csrf.Value+"&name=newrepo&visibility=public&description=new+desc"))
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
req.AddCookie(csrf)
|
|
req.AddCookie(&http.Cookie{Name: webAuthCookie, Value: "tok"})
|
|
s.ServeHTTP(rr, req)
|
|
if rr.Code != http.StatusSeeOther || rr.Header().Get("Location") != "/alice/newrepo" {
|
|
t.Fatalf("new repo status=%d location=%q body=%s", rr.Code, rr.Header().Get("Location"), rr.Body.String())
|
|
}
|
|
|
|
expectLoadRepo(mock, "alice", "demo", repo)
|
|
expectBearerUser(mock, "tok", user)
|
|
rr = httptest.NewRecorder()
|
|
req = httptest.NewRequest(http.MethodPost, "/alice/demo/edit", strings.NewReader("_csrf="+csrf.Value+"&ref=main&path=web.txt&content=from+web"))
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
req.AddCookie(csrf)
|
|
req.AddCookie(&http.Cookie{Name: webAuthCookie, Value: "tok"})
|
|
s.ServeHTTP(rr, req)
|
|
if rr.Code != http.StatusSeeOther || !strings.Contains(rr.Header().Get("Location"), "web.txt") {
|
|
t.Fatalf("edit status=%d location=%q body=%s", rr.Code, rr.Header().Get("Location"), rr.Body.String())
|
|
}
|
|
content, err := gitReadBlob(bare, "main", "web.txt")
|
|
if err != nil || !strings.Contains(content, "from web") {
|
|
t.Fatalf("edited file content=%q err=%v", content, err)
|
|
}
|
|
|
|
privateRepo := repo
|
|
privateRepo.Visibility = "private"
|
|
expectBearerUser(mock, "tok", user)
|
|
expectLoadRepo(mock, "alice", "demo", privateRepo)
|
|
rr = httptest.NewRecorder()
|
|
req = httptest.NewRequest(http.MethodPost, "/alice/demo/fork", strings.NewReader("_csrf="+csrf.Value))
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
req.AddCookie(csrf)
|
|
req.AddCookie(&http.Cookie{Name: webAuthCookie, Value: "tok"})
|
|
s.ServeHTTP(rr, req)
|
|
if rr.Code != http.StatusNotFound {
|
|
t.Fatalf("private fork status=%d body=%s", rr.Code, rr.Body.String())
|
|
}
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|