web frontend for git repositories, written in Go git.pocka.jp/legit.git
3

Configure Feed

Select the types of activity you want to include in your feed.

Add simple tests

These are basis for the future tests.

Shota FUJI (Jul 13, 2026, 11:23 PM +0900) bbeea586 3a07b138

+304
+2
flake.nix
··· 50 50 51 51 vendorHash = "sha256-qSnRQIuHnUQit21232SsMY0LQVvcy0PqBPQVPjsNJWA="; 52 52 53 + nativeBuildInputs = with pkgs; [ git ]; 54 + 53 55 meta.mainProgram = "legit"; 54 56 }; 55 57
+156
routes/git_test.go
··· 1 + // This file tests git operations. 2 + // 3 + // Copyright 2026 Shota FUJI <pockawoooh@gmail.com> 4 + // SPDX-License-Identifier: MIT 5 + 6 + package routes 7 + 8 + import ( 9 + "net/http/httptest" 10 + "net/url" 11 + "os" 12 + "path/filepath" 13 + "testing" 14 + 15 + "github.com/go-git/go-git/v5" 16 + "github.com/go-git/go-git/v5/storage/memory" 17 + "github.com/pocka/legit/config" 18 + "github.com/pocka/legit/embed" 19 + "github.com/pocka/legit/tests" 20 + ) 21 + 22 + func TestGitCloneOK(t *testing.T) { 23 + repos := t.TempDir() 24 + 25 + _, worktree, err := tests.CreateRepository(repos, "foo") 26 + if err != nil { 27 + t.Fatal(err) 28 + } 29 + 30 + readme, err := worktree.Filesystem.Create("README.md") 31 + if err != nil { 32 + t.Fatal(err) 33 + } 34 + 35 + if _, err := readme.Write([]byte("# Foo")); err != nil { 36 + t.Fatal(err) 37 + } 38 + 39 + _ = readme.Close() 40 + 41 + if _, err := worktree.Add("README.md"); err != nil { 42 + t.Fatal(err) 43 + } 44 + 45 + hash, err := worktree.Commit("Add README", &git.CommitOptions{ 46 + Author: tests.SignatureAlice(), 47 + }) 48 + if err != nil { 49 + t.Fatalf("Unable to commit: %s", err) 50 + } 51 + 52 + var c config.Config 53 + c.Repo.ScanPath = repos 54 + c.Repo.Readme = []string{"README.md"} 55 + c.Repo.MainBranch = []string{"trunk"} 56 + 57 + server := httptest.NewServer(Handlers(&c, embed.StaticDir(), embed.TemplatesDir())) 58 + defer server.Close() 59 + 60 + cloneURL, err := url.JoinPath(server.URL, "/foo") 61 + if err != nil { 62 + t.Fatal(err) 63 + } 64 + 65 + cloned, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{ 66 + URL: cloneURL, 67 + ReferenceName: "refs/heads/trunk", 68 + }) 69 + if err != nil { 70 + t.Fatal(err) 71 + } 72 + 73 + commit, err := cloned.CommitObject(hash) 74 + if err != nil { 75 + t.Fatalf("Unable to get commit from the cloned repository: %s", err) 76 + } 77 + 78 + if commit.Message != "Add README" { 79 + t.Fatalf("Unexpected commit message (%s)", commit.Message) 80 + } 81 + } 82 + 83 + func TestGitBareCloneOK(t *testing.T) { 84 + repos := t.TempDir() 85 + 86 + _, worktree, err := tests.CreateRepository(repos, "foo") 87 + if err != nil { 88 + t.Fatal(err) 89 + } 90 + 91 + readme, err := worktree.Filesystem.Create("README.md") 92 + if err != nil { 93 + t.Fatal(err) 94 + } 95 + 96 + readmeContent := `# Foo 97 + 98 + This is foo. 99 + I'm [Markdown](https://commonmark.org/) file for *load* **testing**. 100 + ` 101 + 102 + if _, err := readme.Write([]byte(readmeContent)); err != nil { 103 + t.Fatal(err) 104 + } 105 + 106 + _ = readme.Close() 107 + 108 + if _, err := worktree.Add("README.md"); err != nil { 109 + t.Fatal(err) 110 + } 111 + 112 + hash, err := worktree.Commit("Add README", &git.CommitOptions{ 113 + Author: tests.SignatureAlice(), 114 + }) 115 + if err != nil { 116 + t.Fatalf("Unable to commit: %s", err) 117 + } 118 + 119 + if err := tests.CreateBare(repos, "foo"); err != nil { 120 + t.Fatalf("Unable to create bare repository: %s", err) 121 + } 122 + 123 + if err := os.RemoveAll(filepath.Join(repos, "foo")); err != nil { 124 + t.Fatal(err) 125 + } 126 + 127 + var c config.Config 128 + c.Repo.ScanPath = repos 129 + c.Repo.Readme = []string{"README.md"} 130 + c.Repo.MainBranch = []string{"trunk"} 131 + 132 + server := httptest.NewServer(Handlers(&c, embed.StaticDir(), embed.TemplatesDir())) 133 + defer server.Close() 134 + 135 + cloneURL, err := url.JoinPath(server.URL, "/foo.git") 136 + if err != nil { 137 + t.Fatal(err) 138 + } 139 + 140 + cloned, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{ 141 + URL: cloneURL, 142 + ReferenceName: "refs/heads/trunk", 143 + }) 144 + if err != nil { 145 + t.Fatal(err) 146 + } 147 + 148 + commit, err := cloned.CommitObject(hash) 149 + if err != nil { 150 + t.Fatalf("Unable to get commit from the cloned repository: %s", err) 151 + } 152 + 153 + if commit.Message != "Add README" { 154 + t.Fatalf("Unexpected commit message (%s)", commit.Message) 155 + } 156 + }
+81
routes/routes_test.go
··· 1 + // Copyright 2026 Shota FUJI <pockawoooh@gmail.com> 2 + // SPDX-License-Identifier: MIT 3 + 4 + package routes 5 + 6 + import ( 7 + "io" 8 + "net/http" 9 + "net/http/httptest" 10 + "net/url" 11 + "strings" 12 + "testing" 13 + 14 + "github.com/go-git/go-git/v5" 15 + "github.com/pocka/legit/config" 16 + "github.com/pocka/legit/embed" 17 + "github.com/pocka/legit/tests" 18 + ) 19 + 20 + func TestGetSummaryOK(t *testing.T) { 21 + repos := t.TempDir() 22 + 23 + _, worktree, err := tests.CreateRepository(repos, "foo") 24 + if err != nil { 25 + t.Fatal(err) 26 + } 27 + 28 + readme, err := worktree.Filesystem.Create("README.md") 29 + if err != nil { 30 + t.Fatal(err) 31 + } 32 + 33 + if _, err := readme.Write([]byte("* iawsoiwjfngbhfg812uhjikwe6789asfd")); err != nil { 34 + t.Fatal(err) 35 + } 36 + 37 + _ = readme.Close() 38 + 39 + if _, err := worktree.Add("README.md"); err != nil { 40 + t.Fatal(err) 41 + } 42 + 43 + _, err = worktree.Commit("Add README", &git.CommitOptions{ 44 + Author: tests.SignatureAlice(), 45 + }) 46 + if err != nil { 47 + t.Fatalf("Unable to commit: %s", err) 48 + } 49 + 50 + var c config.Config 51 + c.Repo.ScanPath = repos 52 + c.Repo.Readme = []string{"README.md"} 53 + c.Repo.MainBranch = []string{"trunk"} 54 + 55 + server := httptest.NewServer(Handlers(&c, embed.StaticDir(), embed.TemplatesDir())) 56 + defer server.Close() 57 + 58 + target, err := url.JoinPath(server.URL, "/foo") 59 + if err != nil { 60 + t.Fatal(err) 61 + } 62 + 63 + res, err := http.Get(target) 64 + if err != nil { 65 + t.Fatal(err) 66 + } 67 + 68 + if res.StatusCode != 200 { 69 + t.Fatalf("Expected HTTP 200, Got %d", res.StatusCode) 70 + } 71 + 72 + body, err := io.ReadAll(res.Body) 73 + res.Body.Close() 74 + if err != nil { 75 + t.Fatal(err) 76 + } 77 + 78 + if !strings.Contains(string(body), "<li>iawsoiwjfngbhfg812uhjikwe6789asfd</li>") { 79 + t.Error("Body not containing magic string") 80 + } 81 + }
+65
tests/helper.go
··· 1 + // Copyright 2026 Shota FUJI <pockawoooh@gmail.com> 2 + // SPDX-License-Identifier: MIT 3 + 4 + package tests 5 + 6 + import ( 7 + "fmt" 8 + "net/url" 9 + "path/filepath" 10 + "time" 11 + 12 + "github.com/go-git/go-billy/v5/osfs" 13 + "github.com/go-git/go-git/v5" 14 + "github.com/go-git/go-git/v5/plumbing/cache" 15 + "github.com/go-git/go-git/v5/plumbing/object" 16 + "github.com/go-git/go-git/v5/storage/filesystem" 17 + ) 18 + 19 + func SignatureAlice() *object.Signature { 20 + return &object.Signature{ 21 + Name: "Alice", 22 + Email: "alice@example.com", 23 + When: time.Unix(0, 0), 24 + } 25 + } 26 + 27 + func CreateRepository(baseDir string, name string) (*git.Repository, *git.Worktree, error) { 28 + storage := filesystem.NewStorage( 29 + osfs.New(filepath.Join(baseDir, name), osfs.WithBoundOS()), 30 + cache.NewObjectLRUDefault(), 31 + ) 32 + 33 + repo, err := git.InitWithOptions(storage, storage.Filesystem(), git.InitOptions{ 34 + DefaultBranch: "refs/heads/trunk", 35 + }) 36 + if err != nil { 37 + return nil, nil, fmt.Errorf("Unable to create Git repository: %s", err) 38 + } 39 + 40 + wt, err := repo.Worktree() 41 + if err != nil { 42 + return nil, nil, fmt.Errorf("Unable to get Git worktree: %s", err) 43 + } 44 + 45 + return repo, wt, nil 46 + } 47 + 48 + func CreateBare(baseDir string, name string) error { 49 + storage := filesystem.NewStorage( 50 + osfs.New(filepath.Join(baseDir, fmt.Sprintf("%s.git", name)), osfs.WithBoundOS()), 51 + cache.NewObjectLRUDefault(), 52 + ) 53 + 54 + target := url.URL{ 55 + Scheme: "file", 56 + Path: filepath.Join(baseDir, name), 57 + } 58 + 59 + _, err := git.Clone(storage, nil, &git.CloneOptions{ 60 + URL: target.String(), 61 + ReferenceName: "refs/heads/trunk", 62 + }) 63 + 64 + return err 65 + }