mirror of https://git.jolheiser.com/ugit.git
Compare commits
4 Commits
51f11a9897
...
86aa09929f
Author | SHA1 | Date |
---|---|---|
|
86aa09929f | |
|
b8ca3fc4b8 | |
|
ebe2dc4603 | |
|
ea40aa746e |
|
@ -3,8 +3,10 @@ package git_test
|
|||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/alecthomas/assert/v2"
|
||||
"github.com/go-git/go-git/v5/plumbing/protocol/packp"
|
||||
"go.jolheiser.com/ugit/internal/git"
|
||||
)
|
||||
|
||||
|
@ -43,3 +45,232 @@ func TestRepo(t *testing.T) {
|
|||
assert.NoError(t, err, "should not error when getting existing repo")
|
||||
assert.False(t, repo.Meta.Private, "repo should be public after saving meta")
|
||||
}
|
||||
|
||||
func TestPathExists(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
exists, err := git.PathExists(tmp)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, exists)
|
||||
|
||||
doesNotExist := filepath.Join(tmp, "does-not-exist")
|
||||
exists, err = git.PathExists(doesNotExist)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, exists)
|
||||
}
|
||||
|
||||
func TestRepoMetaUpdate(t *testing.T) {
|
||||
original := git.RepoMeta{
|
||||
Description: "Original description",
|
||||
Private: true,
|
||||
Tags: git.TagSet{"tag1": struct{}{}, "tag2": struct{}{}},
|
||||
}
|
||||
|
||||
update := git.RepoMeta{
|
||||
Description: "Updated description",
|
||||
Private: false,
|
||||
Tags: git.TagSet{"tag3": struct{}{}},
|
||||
}
|
||||
|
||||
err := original.Update(update)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "Updated description", original.Description)
|
||||
assert.False(t, original.Private)
|
||||
assert.Equal(t, []string{"tag1", "tag2", "tag3"}, original.Tags.Slice())
|
||||
}
|
||||
|
||||
func TestFileInfoName(t *testing.T) {
|
||||
testCases := []struct {
|
||||
path string
|
||||
expected string
|
||||
}{
|
||||
{path: "file.txt", expected: "file.txt"},
|
||||
{path: "dir/file.txt", expected: "file.txt"},
|
||||
{path: "nested/path/to/file.go", expected: "file.go"},
|
||||
{path: "README.md", expected: "README.md"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.path, func(t *testing.T) {
|
||||
fi := git.FileInfo{Path: tc.path}
|
||||
assert.Equal(t, tc.expected, fi.Name())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitSummaryAndDetails(t *testing.T) {
|
||||
testCases := []struct {
|
||||
message string
|
||||
expectedSummary string
|
||||
expectedDetails string
|
||||
}{
|
||||
{
|
||||
message: "Simple commit message",
|
||||
expectedSummary: "Simple commit message",
|
||||
expectedDetails: "",
|
||||
},
|
||||
{
|
||||
message: "Add feature X\n\nThis commit adds feature X\nWith multiple details\nAcross multiple lines",
|
||||
expectedSummary: "Add feature X",
|
||||
expectedDetails: "\nThis commit adds feature X\nWith multiple details\nAcross multiple lines",
|
||||
},
|
||||
{
|
||||
message: "Fix bug\n\nDetailed explanation",
|
||||
expectedSummary: "Fix bug",
|
||||
expectedDetails: "\nDetailed explanation",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.message, func(t *testing.T) {
|
||||
commit := git.Commit{
|
||||
SHA: "abcdef1234567890",
|
||||
Message: tc.message,
|
||||
Signature: "",
|
||||
Author: "Test User",
|
||||
Email: "test@example.com",
|
||||
When: time.Now(),
|
||||
}
|
||||
|
||||
assert.Equal(t, tc.expectedSummary, commit.Summary())
|
||||
assert.Equal(t, tc.expectedDetails, commit.Details())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitShort(t *testing.T) {
|
||||
commit := git.Commit{
|
||||
SHA: "abcdef1234567890abcdef1234567890",
|
||||
}
|
||||
|
||||
assert.Equal(t, "abcdef12", commit.Short())
|
||||
}
|
||||
|
||||
func TestCommitFilePath(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fromPath string
|
||||
toPath string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "to path preferred",
|
||||
fromPath: "old/path.txt",
|
||||
toPath: "new/path.txt",
|
||||
expected: "new/path.txt",
|
||||
},
|
||||
{
|
||||
name: "fallback to from path",
|
||||
fromPath: "deleted/file.txt",
|
||||
toPath: "",
|
||||
expected: "deleted/file.txt",
|
||||
},
|
||||
{
|
||||
name: "both paths empty",
|
||||
fromPath: "",
|
||||
toPath: "",
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cf := git.CommitFile{
|
||||
From: git.CommitFileEntry{Path: tc.fromPath},
|
||||
To: git.CommitFileEntry{Path: tc.toPath},
|
||||
}
|
||||
assert.Equal(t, tc.expected, cf.Path())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoName(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
|
||||
repoName := "testrepo"
|
||||
err := git.EnsureRepo(tmp, repoName+".git")
|
||||
assert.NoError(t, err)
|
||||
|
||||
repo, err := git.NewRepo(tmp, repoName)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, repoName, repo.Name())
|
||||
|
||||
repoName2 := "test-repo-with-hyphens"
|
||||
err = git.EnsureRepo(tmp, repoName2+".git")
|
||||
assert.NoError(t, err)
|
||||
|
||||
repo2, err := git.NewRepo(tmp, repoName2)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, repoName2, repo2.Name())
|
||||
}
|
||||
|
||||
func TestHandlePushOptions(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
err := git.EnsureRepo(tmp, "test.git")
|
||||
assert.NoError(t, err)
|
||||
|
||||
repo, err := git.NewRepo(tmp, "test")
|
||||
assert.NoError(t, err)
|
||||
|
||||
opts := []*packp.Option{
|
||||
{Key: "description", Value: "New description"},
|
||||
}
|
||||
err = git.HandlePushOptions(repo, opts)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "New description", repo.Meta.Description)
|
||||
|
||||
opts = []*packp.Option{
|
||||
{Key: "private", Value: "false"},
|
||||
}
|
||||
err = git.HandlePushOptions(repo, opts)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, repo.Meta.Private)
|
||||
|
||||
repo.Meta.Private = true
|
||||
opts = []*packp.Option{
|
||||
{Key: "private", Value: "invalid"},
|
||||
}
|
||||
err = git.HandlePushOptions(repo, opts)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, repo.Meta.Private)
|
||||
|
||||
opts = []*packp.Option{
|
||||
{Key: "tags", Value: "tag1,tag2"},
|
||||
}
|
||||
err = git.HandlePushOptions(repo, opts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
opts = []*packp.Option{
|
||||
{Key: "description", Value: "Combined update"},
|
||||
{Key: "private", Value: "true"},
|
||||
}
|
||||
err = git.HandlePushOptions(repo, opts)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Combined update", repo.Meta.Description)
|
||||
assert.True(t, repo.Meta.Private)
|
||||
}
|
||||
|
||||
func TestRepoPath(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
err := git.EnsureRepo(tmp, "test.git")
|
||||
assert.NoError(t, err)
|
||||
|
||||
repo, err := git.NewRepo(tmp, "test")
|
||||
assert.NoError(t, err)
|
||||
|
||||
expected := filepath.Join(tmp, "test.git")
|
||||
assert.Equal(t, expected, repo.Path())
|
||||
}
|
||||
|
||||
func TestEnsureJSONFile(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
err := git.EnsureRepo(tmp, "test.git")
|
||||
assert.NoError(t, err)
|
||||
|
||||
repo, err := git.NewRepo(tmp, "test")
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.True(t, repo.Meta.Private, "default repo should be private")
|
||||
assert.Equal(t, "", repo.Meta.Description, "default description should be empty")
|
||||
assert.Equal(t, 0, len(repo.Meta.Tags), "default tags should be empty")
|
||||
}
|
||||
|
|
|
@ -7,13 +7,60 @@ import (
|
|||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// RepoMeta is the meta information a Repo can have
|
||||
type RepoMeta struct {
|
||||
Description string `json:"description"`
|
||||
Private bool `json:"private"`
|
||||
Tags []string `json:"tags"`
|
||||
Description string `json:"description"`
|
||||
Private bool `json:"private"`
|
||||
Tags TagSet `json:"tags"`
|
||||
}
|
||||
|
||||
// TagSet is a Set of tags
|
||||
type TagSet map[string]struct{}
|
||||
|
||||
// Add adds a tag to the set
|
||||
func (t TagSet) Add(tag string) {
|
||||
t[tag] = struct{}{}
|
||||
}
|
||||
|
||||
// Remove removes a tag from the set
|
||||
func (t TagSet) Remove(tag string) {
|
||||
delete(t, tag)
|
||||
}
|
||||
|
||||
// Contains checks if a tag is in the set
|
||||
func (t TagSet) Contains(tag string) bool {
|
||||
_, ok := t[tag]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Slice returns the set as a (sorted) slice
|
||||
func (t TagSet) Slice() []string {
|
||||
s := make([]string, 0, len(t))
|
||||
for k := range t {
|
||||
s = append(s, k)
|
||||
}
|
||||
slices.Sort(s)
|
||||
return s
|
||||
}
|
||||
|
||||
// MarshalJSON implements [json.Marshaler]
|
||||
func (t TagSet) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(t.Slice())
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements [json.Unmarshaler]
|
||||
func (t *TagSet) UnmarshalJSON(b []byte) error {
|
||||
var s []string
|
||||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, ss := range s {
|
||||
t.Add(ss)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update updates meta given another RepoMeta
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/alecthomas/assert/v2"
|
||||
)
|
||||
|
||||
func TestTagSet(t *testing.T) {
|
||||
set := make(TagSet)
|
||||
assert.Equal(t, 0, len(set))
|
||||
assert.Equal(t, 0, len(set.Slice()))
|
||||
|
||||
set.Add("foo")
|
||||
assert.Equal(t, 1, len(set))
|
||||
assert.Equal(t, 1, len(set.Slice()))
|
||||
assert.True(t, set.Contains("foo"))
|
||||
|
||||
set.Add("bar")
|
||||
assert.Equal(t, 2, len(set))
|
||||
assert.Equal(t, 2, len(set.Slice()))
|
||||
assert.True(t, set.Contains("foo"))
|
||||
assert.True(t, set.Contains("bar"))
|
||||
|
||||
set.Add("bar")
|
||||
assert.Equal(t, 2, len(set))
|
||||
assert.Equal(t, 2, len(set.Slice()))
|
||||
assert.True(t, set.Contains("foo"))
|
||||
assert.True(t, set.Contains("bar"))
|
||||
|
||||
set.Remove("foo")
|
||||
assert.Equal(t, 1, len(set))
|
||||
assert.Equal(t, 1, len(set.Slice()))
|
||||
assert.False(t, set.Contains("foo"))
|
||||
assert.True(t, set.Contains("bar"))
|
||||
|
||||
set.Add("foo")
|
||||
set.Add("baz")
|
||||
j, err := json.Marshal(set)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `["bar","baz","foo"]`, string(j))
|
||||
|
||||
set = make(TagSet)
|
||||
b := []byte(`["foo","bar","baz"]`)
|
||||
err = json.Unmarshal(b, &set)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(set))
|
||||
assert.Equal(t, 3, len(set.Slice()))
|
||||
assert.True(t, set.Contains("foo"))
|
||||
assert.True(t, set.Contains("bar"))
|
||||
assert.True(t, set.Contains("baz"))
|
||||
}
|
|
@ -58,15 +58,11 @@ func HandlePushOptions(repo *Repo, opts []*packp.Option) error {
|
|||
remove = true
|
||||
tagValue = strings.TrimPrefix(tagValue, "-")
|
||||
}
|
||||
for idx, tag := range repo.Meta.Tags {
|
||||
if strings.EqualFold(tag, tagValue) {
|
||||
if remove {
|
||||
repo.Meta.Tags = append(repo.Meta.Tags[:idx], repo.Meta.Tags[idx+1:]...)
|
||||
} else {
|
||||
repo.Meta.Tags = append(repo.Meta.Tags, strings.ToLower(tagValue))
|
||||
}
|
||||
break
|
||||
}
|
||||
tagValue = strings.ToLower(tagValue)
|
||||
if remove {
|
||||
repo.Meta.Tags.Remove(tagValue)
|
||||
} else {
|
||||
repo.Meta.Tags.Add(tagValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,9 @@ func NewRepo(dir, name string) (*Repo, error) {
|
|||
if err := json.NewDecoder(fi).Decode(&r.Meta); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.Meta.Tags == nil {
|
||||
r.Meta.Tags = make(TagSet)
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package html
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"go.jolheiser.com/ugit/assets"
|
||||
"go.jolheiser.com/ugit/internal/git"
|
||||
|
@ -25,7 +27,7 @@ type IndexLink struct {
|
|||
URL string
|
||||
}
|
||||
|
||||
func lastCommit(repo *git.Repo, human bool) string {
|
||||
func lastCommitTime(repo *git.Repo, human bool) string {
|
||||
c, err := repo.LastCommit()
|
||||
if err != nil {
|
||||
return ""
|
||||
|
@ -36,6 +38,14 @@ func lastCommit(repo *git.Repo, human bool) string {
|
|||
return c.When.Format("01/02/2006 03:04:05 PM")
|
||||
}
|
||||
|
||||
func lastCommit(repo *git.Repo) *git.Commit {
|
||||
c, err := repo.LastCommit()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &c
|
||||
}
|
||||
|
||||
func IndexTemplate(ic IndexContext) Node {
|
||||
return base(ic.BaseContext, []Node{
|
||||
Header(
|
||||
|
@ -65,19 +75,28 @@ func IndexTemplate(ic IndexContext) Node {
|
|||
)
|
||||
}),
|
||||
),
|
||||
Div(Class("grid sm:grid-cols-8 gap-2 mt-5"),
|
||||
Div(Class("grid sm:grid-cols-10 gap-2 mt-5"),
|
||||
Map(ic.Repos, func(repo *git.Repo) Node {
|
||||
commit := lastCommit(repo)
|
||||
return Group([]Node{
|
||||
Div(Class("sm:col-span-2 text-blue dark:text-lavender"),
|
||||
A(Class("underline decoration-blue/50 dark:decoration-lavender/50 decoration-dashed hover:decoration-solid"), Href("/"+repo.Name()), Text(repo.Name())),
|
||||
),
|
||||
Div(Class("sm:col-span-4 text-subtext0"), Text(repo.Meta.Description)),
|
||||
Div(Class("sm:col-span-3 text-subtext0"), Text(repo.Meta.Description)),
|
||||
Div(Class("sm:col-span-3 text-subtext0"),
|
||||
If(commit != nil,
|
||||
Div(Title(commit.Message),
|
||||
A(Class("underline text-blue dark:text-lavender decoration-blue/50 dark:decoration-lavender/50 decoration-dashed hover:decoration-solid"), Href(fmt.Sprintf("/%s/commit/%s", repo.Name(), commit.SHA)), Text(commit.Short())),
|
||||
Text(": "+commit.Summary()),
|
||||
),
|
||||
),
|
||||
),
|
||||
Div(Class("sm:col-span-1 text-subtext0"),
|
||||
Map(repo.Meta.Tags, func(tag string) Node {
|
||||
Map(repo.Meta.Tags.Slice(), func(tag string) Node {
|
||||
return A(Class("rounded border-rosewater border-solid border pb-0.5 px-1 mr-1 mb-1 inline-block"), Href("?tag="+tag), Text(tag))
|
||||
}),
|
||||
),
|
||||
Div(Class("sm:col-span-1 text-text/80 mb-4 sm:mb-0"), Title(lastCommit(repo, false)), Text(lastCommit(repo, true))),
|
||||
Div(Class("sm:col-span-1 text-text/80 mb-4 sm:mb-0"), Title(lastCommitTime(repo, false)), Text(lastCommitTime(repo, true))),
|
||||
})
|
||||
}),
|
||||
),
|
||||
|
|
|
@ -21,6 +21,7 @@ type RepoFileContext struct {
|
|||
var repoFileJS string
|
||||
|
||||
func RepoFileTemplate(rfc RepoFileContext) Node {
|
||||
permalink := fmt.Sprintf("/%s/tree/%s/%s", rfc.RepoBreadcrumbComponentContext.Repo, rfc.Commit, rfc.Path)
|
||||
return base(rfc.BaseContext, []Node{
|
||||
repoHeaderComponent(rfc.RepoHeaderComponentContext),
|
||||
Div(Class("mt-2 text-text"),
|
||||
|
@ -28,7 +29,7 @@ func RepoFileTemplate(rfc RepoFileContext) Node {
|
|||
Text(" - "),
|
||||
A(Class("text-text underline decoration-text/50 decoration-dashed hover:decoration-solid"), Href("?raw"), Text("raw")),
|
||||
Text(" - "),
|
||||
A(Class("text-text underline decoration-text/50 decoration-dashed hover:decoration-solid"), ID("permalink"), Href(fmt.Sprintf("/%s/tree/%s/%s", rfc.RepoBreadcrumbComponentContext.Repo, rfc.Commit, rfc.Path)), Text("permalink")),
|
||||
A(Class("text-text underline decoration-text/50 decoration-dashed hover:decoration-solid"), ID("permalink"), Data("permalink", permalink), Href(permalink), Text("permalink")),
|
||||
Div(Class("code relative"),
|
||||
Raw(rfc.Code),
|
||||
Button(ID("copy"), Class("absolute top-0 right-0 rounded bg-base hover:bg-surface0")),
|
||||
|
|
|
@ -2,7 +2,7 @@ const lineRe = /#L(\d+)(?:-L(\d+))?/g
|
|||
const $lineLines = document.querySelectorAll(".chroma .lntable .lnt");
|
||||
const $codeLines = document.querySelectorAll(".chroma .lntable .line");
|
||||
const $copyButton = document.getElementById('copy');
|
||||
const $permalinkButton = document.getElementById('permalink');
|
||||
const $permalink = document.getElementById('permalink');
|
||||
const $copyIcon = "📋";
|
||||
const $copiedIcon = "✅";
|
||||
let $code = ""
|
||||
|
@ -14,9 +14,12 @@ if (0 in results) {
|
|||
start = results[0][1] !== undefined ? parseInt(results[0][1]) : 0;
|
||||
end = results[0][2] !== undefined ? parseInt(results[0][2]) : 0;
|
||||
}
|
||||
if (start != 0) {
|
||||
if (start !== 0) {
|
||||
deactivateLines();
|
||||
activateLines(start, end);
|
||||
let anchor = `#${start}`;
|
||||
if (end !== 0) anchor += `-${end}`;
|
||||
if (anchor !== "") $permalink.href = $permalink.dataset.permalink + anchor;
|
||||
$lineLines[start - 1].scrollIntoView(true);
|
||||
}
|
||||
for (let line of $lineLines) {
|
||||
|
@ -28,13 +31,17 @@ for (let line of $lineLines) {
|
|||
if (event.shiftKey) {
|
||||
end = n;
|
||||
anchor = `#L${start}-L${end}`;
|
||||
} else if (start === n) {
|
||||
start = 0;
|
||||
end = 0;
|
||||
} else {
|
||||
start = n;
|
||||
end = 0;
|
||||
anchor = `#L${start}`;
|
||||
}
|
||||
history.replaceState(null, null, anchor);
|
||||
activateLines(start, end);
|
||||
history.replaceState(null, null, window.location.pathname + anchor);
|
||||
$permalink.href = $permalink.dataset.permalink + anchor;
|
||||
if (start !== 0) activateLines(start, end);
|
||||
});
|
||||
}
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
|
@ -49,18 +56,6 @@ $copyButton.addEventListener("click", () => {
|
|||
}, 1000);
|
||||
});
|
||||
|
||||
$permalinkButton.addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
const url = $permalinkButton.getAttribute("href");
|
||||
navigator.clipboard.writeText(window.location.origin + url + location.hash);
|
||||
|
||||
const originalText = $permalinkButton.innerText;
|
||||
$permalinkButton.innerText = "copied!";
|
||||
setTimeout(() => {
|
||||
$permalinkButton.innerText = originalText;
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
function activateLines(start, end) {
|
||||
if (end < start) end = start;
|
||||
for (let idx = start - 1; idx < end; idx++) {
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -126,7 +126,7 @@ func (rh repoHandler) repoHeaderContext(repo *git.Repo, r *http.Request) html.Re
|
|||
Name: chi.URLParam(r, "repo"),
|
||||
Ref: ref,
|
||||
CloneURL: rh.s.CloneURL,
|
||||
Tags: repo.Meta.Tags,
|
||||
Tags: repo.Meta.Tags.Slice(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
package httperr_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/alecthomas/assert/v2"
|
||||
"go.jolheiser.com/ugit/internal/http/httperr"
|
||||
)
|
||||
|
||||
func successHandler(w http.ResponseWriter, r *http.Request) error {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return nil
|
||||
}
|
||||
|
||||
func errorHandler(w http.ResponseWriter, r *http.Request) error {
|
||||
return errors.New("test error")
|
||||
}
|
||||
|
||||
func statusErrorHandler(status int) func(w http.ResponseWriter, r *http.Request) error {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
return httperr.Status(errors.New("test error"), status)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandler_Success(t *testing.T) {
|
||||
handler := httperr.Handler(successHandler)
|
||||
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(recorder, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, recorder.Code)
|
||||
}
|
||||
|
||||
func TestHandler_Error(t *testing.T) {
|
||||
handler := httperr.Handler(errorHandler)
|
||||
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(recorder, req)
|
||||
|
||||
assert.Equal(t, http.StatusInternalServerError, recorder.Code)
|
||||
}
|
||||
|
||||
func TestHandler_StatusError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
status int
|
||||
expectedStatus int
|
||||
}{
|
||||
{
|
||||
name: "not found",
|
||||
status: http.StatusNotFound,
|
||||
expectedStatus: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
name: "bad request",
|
||||
status: http.StatusBadRequest,
|
||||
expectedStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "unauthorized",
|
||||
status: http.StatusUnauthorized,
|
||||
expectedStatus: http.StatusUnauthorized,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
handler := httperr.Handler(statusErrorHandler(tc.status))
|
||||
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(recorder, req)
|
||||
|
||||
assert.Equal(t, tc.expectedStatus, recorder.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type unwrapper interface {
|
||||
Unwrap() error
|
||||
}
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
originalErr := errors.New("original error")
|
||||
httpErr := httperr.Error(originalErr)
|
||||
|
||||
assert.Equal(t, originalErr.Error(), httpErr.Error())
|
||||
|
||||
unwrapper, ok := any(httpErr).(unwrapper)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, originalErr, unwrapper.Unwrap())
|
||||
}
|
||||
|
||||
func TestStatus(t *testing.T) {
|
||||
originalErr := errors.New("original error")
|
||||
httpErr := httperr.Status(originalErr, http.StatusNotFound)
|
||||
|
||||
assert.Equal(t, originalErr.Error(), httpErr.Error())
|
||||
|
||||
unwrapper, ok := any(httpErr).(unwrapper)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, originalErr, unwrapper.Unwrap())
|
||||
|
||||
handler := httperr.Handler(func(w http.ResponseWriter, r *http.Request) error {
|
||||
return httpErr
|
||||
})
|
||||
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(recorder, req)
|
||||
|
||||
assert.Equal(t, http.StatusNotFound, recorder.Code)
|
||||
}
|
|
@ -3,7 +3,6 @@ package http
|
|||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -34,10 +33,10 @@ func (rh repoHandler) index(w http.ResponseWriter, r *http.Request) error {
|
|||
if !rh.s.ShowPrivate {
|
||||
continue
|
||||
}
|
||||
repo.Meta.Tags = append(repo.Meta.Tags, "private")
|
||||
repo.Meta.Tags.Add("private")
|
||||
}
|
||||
|
||||
if tagFilter != "" && !slices.Contains(repo.Meta.Tags, strings.ToLower(tagFilter)) {
|
||||
if tagFilter != "" && !repo.Meta.Tags.Contains(strings.ToLower(tagFilter)) {
|
||||
continue
|
||||
}
|
||||
repos = append(repos, repo)
|
||||
|
|
|
@ -30,7 +30,7 @@ func (rh repoHandler) repoMiddleware(next http.Handler) http.Handler {
|
|||
if !rh.s.ShowPrivate {
|
||||
return httperr.Status(errors.New("could not get git repo"), http.StatusNotFound)
|
||||
}
|
||||
repo.Meta.Tags = append(repo.Meta.Tags, "private")
|
||||
repo.Meta.Tags.Add("private")
|
||||
}
|
||||
r = r.WithContext(context.WithValue(r.Context(), repoCtxKey, repo))
|
||||
next.ServeHTTP(w, r)
|
||||
|
|
Loading…
Reference in New Issue