fierj
PublicTiny personal git forge
package main
import (
"net/http"
"strings"
"testing"
)
// ---------- Repository tree, blob, log, diff ----------
// TestTreeSubdirectories verifies navigating into nested directories.
func TestTreeSubdirectories(t *testing.T) {
ts := newTestServer(t)
c := ts.client()
ts.setupUser(t, c, "dev", "secret123")
ts.createRepo(t, c, "nested", "Nested dirs")
localDir := t.TempDir()
bareRepo := repoPath(ts.cfg, "nested")
gitCmd(t, localDir, "clone", "file://"+bareRepo, "repo")
localRepo := localDir + "/repo"
// Create nested structure: src/lib/util.go
writeFile(t, localRepo, "src/lib/util.go", "package lib\n")
gitCmd(t, localRepo, "add", "src/lib/util.go")
gitCmd(t, localRepo, "commit", "-m", "add util")
gitCmd(t, localRepo, "push", "origin", "main")
// Root tree should show src/
resp := ts.get(t, c, "/nested")
assertStatus(t, resp, http.StatusOK)
b := body(t, resp)
if !strings.Contains(b, "src") {
t.Error("root tree should show src directory")
}
// Navigate into src/
resp = ts.get(t, c, "/nested/tree/main/src")
assertStatus(t, resp, http.StatusOK)
b = body(t, resp)
if !strings.Contains(b, "lib") {
t.Error("src/ tree should show lib directory")
}
// Navigate into src/lib/
resp = ts.get(t, c, "/nested/tree/main/src/lib")
assertStatus(t, resp, http.StatusOK)
b = body(t, resp)
if !strings.Contains(b, "util.go") {
t.Error("src/lib/ tree should show util.go")
}
// View blob
resp = ts.get(t, c, "/nested/blob/main/src/lib/util.go")
assertStatus(t, resp, http.StatusOK)
b = body(t, resp)
if !strings.Contains(b, "package lib") {
t.Error("blob should show file content")
}
}
// TestCommitDiff verifies the commit diff view (was broken by --,hash bug).
func TestCommitDiff(t *testing.T) {
ts := newTestServer(t)
c := ts.client()
ts.setupUser(t, c, "dev", "secret123")
ts.createRepo(t, c, "diffrepo", "Diff test")
localDir := t.TempDir()
bareRepo := repoPath(ts.cfg, "diffrepo")
gitCmd(t, localDir, "clone", "file://"+bareRepo, "repo")
localRepo := localDir + "/repo"
writeFile(t, localRepo, "hello.txt", "hello world\n")
gitCmd(t, localRepo, "add", "hello.txt")
gitCmd(t, localRepo, "commit", "-m", "add hello")
gitCmd(t, localRepo, "push", "origin", "main")
// Get the commit hash
hash := strings.TrimSpace(gitCmd(t, localRepo, "rev-parse", "HEAD"))
// View the commit diff
resp := ts.get(t, c, "/diffrepo/commit/"+hash)
assertStatus(t, resp, http.StatusOK)
b := body(t, resp)
if !strings.Contains(b, "hello world") {
t.Error("commit diff should show file content")
}
if !strings.Contains(b, hash[:8]) {
t.Error("commit diff page should show the hash")
}
// Short hash (8 chars) should also work
shortHash := hash[:8]
resp = ts.get(t, c, "/diffrepo/commit/"+shortHash)
assertStatus(t, resp, http.StatusOK)
}
// TestTreeShowsLastCommit verifies files show their last commit message and date.
func TestTreeShowsLastCommit(t *testing.T) {
ts := newTestServer(t)
c := ts.client()
ts.setupUser(t, c, "dev", "secret123")
ts.createRepo(t, c, "lastcommit", "Last commit test")
localDir := t.TempDir()
bareRepo := repoPath(ts.cfg, "lastcommit")
gitCmd(t, localDir, "clone", "file://"+bareRepo, "repo")
localRepo := localDir + "/repo"
writeFile(t, localRepo, "a.txt", "file a\n")
gitCmd(t, localRepo, "add", "a.txt")
gitCmd(t, localRepo, "commit", "-m", "add file a")
gitCmd(t, localRepo, "push", "origin", "main")
writeFile(t, localRepo, "b.txt", "file b\n")
gitCmd(t, localRepo, "add", "b.txt")
gitCmd(t, localRepo, "commit", "-m", "add file b")
gitCmd(t, localRepo, "push", "origin", "main")
// Tree should show last commit info for each file
resp := ts.get(t, c, "/lastcommit")
assertStatus(t, resp, http.StatusOK)
b := body(t, resp)
// Both files should appear
if !strings.Contains(b, "a.txt") {
t.Error("tree should show a.txt")
}
if !strings.Contains(b, "b.txt") {
t.Error("tree should show b.txt")
}
// Each file should have commit author relative dates
// "add file a" or "add file b" messages
if !strings.Contains(b, "add file a") && !strings.Contains(b, "add file b") {
t.Error("tree should show last commit messages for files")
}
}
// TestRepoNameCollision verifies duplicate repo creation returns error.
func TestRepoNameCollision(t *testing.T) {
ts := newTestServer(t)
c := ts.client()
ts.setupUser(t, c, "dev", "secret123")
ts.createRepo(t, c, "unique", "First")
// Try to create another with same name
resp := ts.postForm(t, c, "/new", map[string][]string{
"name": {"unique"},
"description": {"Second attempt"},
})
assertStatus(t, resp, http.StatusOK)
b := body(t, resp)
// Duplicate repos currently succeed (InitRepo reinitializes).
// Check that the repo page renders.
if !strings.Contains(b, "unique") {
t.Error("redirect should go to the repo page")
}
}
// TestInvalidRepoName verifies bad repo names are rejected.
func TestInvalidRepoName(t *testing.T) {
ts := newTestServer(t)
c := ts.client()
ts.setupUser(t, c, "dev", "secret123")
for _, name := range []string{"", "a/b", "..", "../escape"} {
resp := ts.postForm(t, c, "/new", map[string][]string{
"name": {name},
"description": {"bad"},
})
// Should return error (400 or redirect to error page)
b := body(t, resp)
if !strings.Contains(b, "invalid") && !strings.Contains(b, "failed") && !strings.Contains(b, "required") {
t.Logf("repo name %q returned status %d, body snippet: %.100s", name, resp.StatusCode, b)
}
}
}
// TestEmptyRepoRefsPage verifies refs page renders for empty repos.
func TestEmptyRepoRefsPage(t *testing.T) {
ts := newTestServer(t)
c := ts.client()
ts.setupUser(t, c, "dev", "secret123")
ts.createRepo(t, c, "emptyrefs", "Empty refs")
resp := ts.get(t, c, "/emptyrefs/refs/")
assertStatus(t, resp, http.StatusOK)
// Should render without crashing
b := body(t, resp)
if !strings.Contains(b, "emptyrefs") {
t.Error("empty refs page should render without error")
}
}
// TestBranchDeletionAfterMerge verifies merged branches are cleaned up.
func TestBranchDeletionAfterMerge(t *testing.T) {
ts := newTestServer(t)
c := ts.client()
ts.setupUser(t, c, "dev", "secret123")
ts.createRepo(t, c, "cleanup", "Branch cleanup")
localDir := t.TempDir()
bareRepo := repoPath(ts.cfg, "cleanup")
gitCmd(t, localDir, "clone", "file://"+bareRepo, "repo")
localRepo := localDir + "/repo"
writeFile(t, localRepo, "f.txt", "v1")
gitCmd(t, localRepo, "add", "f.txt")
gitCmd(t, localRepo, "commit", "-m", "init")
gitCmd(t, localRepo, "push", "origin", "main")
// Create and push a feature branch
gitCmd(t, localRepo, "checkout", "-b", "to-merge")
writeFile(t, localRepo, "f.txt", "v2")
gitCmd(t, localRepo, "add", "f.txt")
gitCmd(t, localRepo, "commit", "-m", "improve")
gitCmd(t, localRepo, "push", "origin", "to-merge")
// Verify the branch exists on server
refs := gitCmd(t, ts.dir, "--git-dir="+bareRepo, "for-each-ref", "refs/heads/")
if !strings.Contains(refs, "to-merge") {
t.Fatal("branch should exist before merge")
}
// Create and merge patch
rp := repoPath(ts.cfg, "cleanup")
patches, _ := listPatches(rp, "open")
// Create patch if none exist (branches may appear as free branches)
ts.postForm(t, c, "/cleanup/patches/new", map[string][]string{
"title": {"Merge me"},
"body": {"Body"},
"branch": {"to-merge"},
})
patches, _ = listPatches(rp, "open")
if len(patches) != 1 {
t.Fatalf("expected 1 patch, got %d", len(patches))
}
ts.postForm(t, c, "/cleanup/patches/"+patches[0].ID+"/merge", map[string][]string{})
// Branch should be deleted after merge
refs = gitCmd(t, ts.dir, "--git-dir="+bareRepo, "for-each-ref", "refs/heads/")
if strings.Contains(refs, "to-merge") {
t.Error("merged branch should be deleted")
}
}
// TestTagsListedOnRefsPage verifies tags appear on the refs page.
func TestTagsListedOnRefsPage(t *testing.T) {
ts := newTestServer(t)
c := ts.client()
ts.setupUser(t, c, "dev", "secret123")
ts.createRepo(t, c, "tagged", "Tagged repo")
localDir := t.TempDir()
bareRepo := repoPath(ts.cfg, "tagged")
gitCmd(t, localDir, "clone", "file://"+bareRepo, "repo")
localRepo := localDir + "/repo"
writeFile(t, localRepo, "f.txt", "tag test")
gitCmd(t, localRepo, "add", "f.txt")
gitCmd(t, localRepo, "commit", "-m", "init")
gitCmd(t, localRepo, "push", "origin", "main")
gitCmd(t, localRepo, "tag", "-a", "v1.0.0", "-m", "first tag")
gitCmd(t, localRepo, "push", "origin", "v1.0.0")
resp := ts.get(t, c, "/tagged/refs/")
assertStatus(t, resp, http.StatusOK)
b := body(t, resp)
if !strings.Contains(b, "v1.0.0") {
t.Error("refs page should list tags")
}
}
// TestTagTreeView verifies browsing a tree at a tag.
func TestTagTreeView(t *testing.T) {
ts := newTestServer(t)
c := ts.client()
ts.setupUser(t, c, "dev", "secret123")
ts.createRepo(t, c, "tagview", "Tag view")
localDir := t.TempDir()
bareRepo := repoPath(ts.cfg, "tagview")
gitCmd(t, localDir, "clone", "file://"+bareRepo, "repo")
localRepo := localDir + "/repo"
writeFile(t, localRepo, "README.md", "# v1\n")
gitCmd(t, localRepo, "add", "README.md")
gitCmd(t, localRepo, "commit", "-m", "first")
gitCmd(t, localRepo, "push", "origin", "main")
gitCmd(t, localRepo, "tag", "-a", "v0.1.0", "-m", "release")
gitCmd(t, localRepo, "push", "origin", "v0.1.0")
// Change file after tag
writeFile(t, localRepo, "README.md", "# v2\n")
gitCmd(t, localRepo, "add", "README.md")
gitCmd(t, localRepo, "commit", "-m", "second")
gitCmd(t, localRepo, "push", "origin", "main")
// Tree at tag should show old content
resp := ts.get(t, c, "/tagview/tree/v0.1.0")
assertStatus(t, resp, http.StatusOK)
b := body(t, resp)
// Blob at tag
resp = ts.get(t, c, "/tagview/blob/v0.1.0/README.md")
assertStatus(t, resp, http.StatusOK)
b = body(t, resp)
if !strings.Contains(b, "# v1") {
t.Error("tag blob should show v1 content")
}
if strings.Contains(b, "# v2") {
t.Error("tag blob should not show v2 content")
}
}