Parent directory

history_test.go

4980 bytes
  1package pages
  2
  3import (
  4	"bytes"
  5	"os"
  6	"os/exec"
  7	"path/filepath"
  8	"strings"
  9	"testing"
 10
 11	"github.com/charmbracelet/soft-serve/git"
 12)
 13
 14func TestHistoryPaginationUsesThirtyItemsAndBounds(t *testing.T) {
 15	repo := newTestRepository(t, map[string]string{"log.txt": "0\n"})
 16	for i := 1; i <= commitsPerPage; i++ {
 17		appendCommit(t, repo.Path, "log.txt", "next "+strings.Repeat("x", i))
 18	}
 19	ref, err := ResolveRef(repo, "")
 20	if err != nil {
 21		t.Fatal(err)
 22	}
 23	commits, err := repo.CommitsByPage(ref, 1, commitsPerPage+1)
 24	if err != nil {
 25		t.Fatal(err)
 26	}
 27	if len(commits) != commitsPerPage+1 {
 28		t.Fatalf("extra commit request returned %d commits, want %d", len(commits), commitsPerPage+1)
 29	}
 30	if got := len(commitEntries("project", commits[:commitsPerPage])); got != commitsPerPage {
 31		t.Fatalf("display entries = %d, want %d", got, commitsPerPage)
 32	}
 33	if _, err := historyPageNumber("0"); err == nil {
 34		t.Fatal("zero page was accepted")
 35	}
 36	if got, err := historyPageNumber("1001"); err != nil || got != maxCommitPage {
 37		t.Fatalf("out-of-range page = %d, %v; want %d, nil", got, err, maxCommitPage)
 38	}
 39}
 40
 41func TestRefsSupportSlashBranchesAndAnnotatedTags(t *testing.T) {
 42	repo := newTestRepository(t, map[string]string{"README.md": "hello"})
 43	runGit(t, repo.Path, "branch", "feature/with-slash")
 44	runGit(t, repo.Path, "tag", "-a", "release/v1", "-m", "release")
 45	references, err := repo.References()
 46	if err != nil {
 47		t.Fatal(err)
 48	}
 49	var branch, tag *git.Reference
 50	for _, ref := range references {
 51		switch ref.Name().String() {
 52		case "refs/heads/feature/with-slash":
 53			branch = ref
 54		case "refs/tags/release/v1":
 55			tag = ref
 56		}
 57	}
 58	if branch == nil || tag == nil {
 59		t.Fatalf("missing branch or tag in %#v", references)
 60	}
 61	branchEntry := refEntry(repo, "team/project", branch)
 62	tagEntry := refEntry(repo, "team/project", tag)
 63	for _, entry := range []refPageEntry{branchEntry, tagEntry} {
 64		if entry.CommitURL == "" || !strings.Contains(entry.CommitURL, "/@/commit?hash=") {
 65			t.Fatalf("commit link missing: %#v", entry)
 66		}
 67	}
 68	if !strings.Contains(branchEntry.TreeURL, "ref=refs%2Fheads%2Ffeature%2Fwith-slash") {
 69		t.Fatalf("branch tree link does not encode full ref: %q", branchEntry.TreeURL)
 70	}
 71	if !strings.Contains(tagEntry.TreeURL, "ref=refs%2Ftags%2Frelease%2Fv1") {
 72		t.Fatalf("tag tree link does not encode full ref: %q", tagEntry.TreeURL)
 73	}
 74}
 75
 76func TestCommitLookupValidationAndRepositoryBoundary(t *testing.T) {
 77	first := newTestRepository(t, map[string]string{"first.txt": "first"})
 78	second := newTestRepository(t, map[string]string{"second.txt": "second"})
 79	if _, err := LookupCommit(first, "not-a-hash"); err == nil {
 80		t.Fatal("invalid hash was accepted")
 81	}
 82	treeID := strings.TrimSpace(runGitOutput(t, first.Path, "rev-parse", "HEAD^{tree}"))
 83	if _, err := LookupCommit(first, treeID); err == nil {
 84		t.Fatal("non-commit object was accepted")
 85	}
 86	secondID := strings.TrimSpace(runGitOutput(t, second.Path, "rev-parse", "HEAD"))
 87	if _, err := LookupCommit(first, secondID); err == nil {
 88		t.Fatal("commit from another repository was disclosed")
 89	}
 90}
 91
 92func TestCommitMessageEscapingAndBoundedDiff(t *testing.T) {
 93	repo := newTestRepository(t, map[string]string{"changed.txt": "start\n"})
 94	appendCommit(t, repo.Path, "changed.txt", strings.Repeat("line\n", 120))
 95	hash := strings.TrimSpace(runGitOutput(t, repo.Path, "rev-parse", "HEAD"))
 96	commit, err := LookupCommit(repo, hash)
 97	if err != nil {
 98		t.Fatal(err)
 99	}
100	diff, err := repo.DiffWithLimits(commit, git.DiffLimits{MaxFiles: maxDiffFiles, MaxFileLines: maxDiffFileLines, MaxLineChars: maxDiffLineChars})
101	if err != nil {
102		t.Fatal(err)
103	}
104	patch, truncated, err := boundedDiffPatch(diff)
105	if err != nil {
106		t.Fatal(err)
107	}
108	if !truncated || strings.Count(patch, "\n") > maxDiffFileLines+5 {
109		t.Fatalf("diff was not bounded: truncated=%t lines=%d", truncated, strings.Count(patch, "\n"))
110	}
111	var rendered bytes.Buffer
112	if err := pageTemplates.ExecuteTemplate(&rendered, "commit", commitPage{Message: "<script>alert(1)</script>"}); err != nil {
113		t.Fatal(err)
114	}
115	if strings.Contains(rendered.String(), "<script>alert(1)</script>") || !strings.Contains(rendered.String(), "&lt;script&gt;") {
116		t.Fatalf("commit message was not escaped: %s", rendered.String())
117	}
118}
119
120func appendCommit(t *testing.T, repoPath, filename, content string) {
121	t.Helper()
122	appendCommitMessage(t, repoPath, filename, content, "change")
123}
124
125func appendCommitMessage(t *testing.T, repoPath, filename, content, message string) {
126	t.Helper()
127	if err := os.WriteFile(filepath.Join(repoPath, filename), []byte(content), 0o600); err != nil {
128		t.Fatal(err)
129	}
130	runGit(t, repoPath, "add", filename)
131	runGit(t, repoPath, "commit", "-qm", message)
132}
133
134func runGitOutput(t *testing.T, dir string, args ...string) string {
135	t.Helper()
136	cmd := exec.Command("git", args...)
137	cmd.Dir = dir
138	output, err := cmd.CombinedOutput()
139	if err != nil {
140		t.Fatalf("git %s: %v: %s", strings.Join(args, " "), err, output)
141	}
142	return string(output)
143}