1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package domain_test
4
5import (
6 "strings"
7 "testing"
8
9 "github.com/stretchr/testify/assert"
10 "github.com/stretchr/testify/require"
11
12 "bnema.dev/ermite/internal/domain"
13)
14
15func TestParseObjectID(t *testing.T) {
16 good := strings.Repeat("ab12", 10)
17 id, err := domain.ParseObjectID(good)
18 require.NoError(t, err)
19 assert.Equal(t, good, id.String())
20 assert.False(t, id.IsZero())
21 assert.True(t, domain.ObjectID{}.IsZero())
22
23 for _, bad := range []string{"", "abc", strings.ToUpper(good), good + "0", good[:39], strings.Repeat("g", 40), "main"} {
24 _, err := domain.ParseObjectID(bad)
25 assert.ErrorIs(t, err, domain.ErrInvalidObjectID, bad)
26 }
27}
28
29func TestParseRevision(t *testing.T) {
30 valid := []string{
31 "", "main", "feature/x", "refs/heads/feature/x", "v1.0.0", "HEAD",
32 "abc1234", strings.Repeat("a", 40), "release-2026.10", "café",
33 }
34 for _, s := range valid {
35 r, err := domain.ParseRevision(s)
36 require.NoError(t, err, s)
37 assert.Equal(t, s, r.String())
38 }
39 invalid := []string{
40 "-x", "--upload-pack=x", "/x", "x/", "x.", "a..b", "a@{1}", "@", "a b", "a~1", "a^", "a:b", "a?", "a*", "a[",
41 "a\\b", "a\nb", "a\x00b", "a//b", ".hidden", "a/.b", "x.lock", "a/x.lock/b", "\xff", strings.Repeat("a", 513),
42 }
43 for _, s := range invalid {
44 _, err := domain.ParseRevision(s)
45 assert.ErrorIs(t, err, domain.ErrInvalidRevision, "%q", s)
46 }
47}
48
49func TestRevisionKinds(t *testing.T) {
50 head, _ := domain.ParseRevision("")
51 assert.True(t, head.IsHead())
52 full, _ := domain.ParseRevision(strings.Repeat("a", 40))
53 assert.True(t, full.IsObjectID())
54 assert.False(t, full.IsAbbrevID())
55 short, _ := domain.ParseRevision("abcdef1")
56 assert.True(t, short.IsAbbrevID())
57 tooShort, _ := domain.ParseRevision("abcdef")
58 assert.False(t, tooShort.IsAbbrevID())
59 branch, _ := domain.ParseRevision("deadbeefx")
60 assert.False(t, branch.IsAbbrevID())
61
62 id, _ := domain.ParseObjectID(strings.Repeat("c", 40))
63 assert.True(t, domain.RevisionOf(id).IsObjectID())
64}
65
66func TestParseTreePath(t *testing.T) {
67 root, err := domain.ParseTreePath("")
68 require.NoError(t, err)
69 assert.True(t, root.IsRoot())
70 assert.Equal(t, "x", root.Join("x"))
71
72 p, err := domain.ParseTreePath("a/b/")
73 require.NoError(t, err)
74 assert.Equal(t, "a/b", p.String())
75 assert.Equal(t, "a/b/c", p.Join("c"))
76
77 for _, bad := range []string{"/a", "a//b", "./a", "a/..", "..", ".", "a\x00b", "a\nb", "\xff", strings.Repeat("a", 4097), "//"} {
78 _, err := domain.ParseTreePath(bad)
79 assert.ErrorIs(t, err, domain.ErrInvalidPath, "%q", bad)
80 }
81}
82
83func TestEntryKindOf(t *testing.T) {
84 tests := map[uint32]domain.EntryKind{
85 0o100644: domain.EntryFile,
86 0o100664: domain.EntryFile,
87 0o100755: domain.EntryExecutable,
88 0o040000: domain.EntryDirectory,
89 0o120000: domain.EntrySymlink,
90 0o160000: domain.EntrySubmodule,
91 0o000000: "",
92 0o060000: "",
93 }
94 for mode, want := range tests {
95 assert.Equal(t, want, domain.EntryKindOf(mode), "%o", mode)
96 }
97}
98
99func TestDefaultDiffLimits(t *testing.T) {
100 assert.Equal(t, domain.DiffLimits{MaxFiles: 300, MaxFileLines: 2000, MaxTotalLines: 20000, MaxTotalBytes: 8 << 20}, domain.DefaultDiffLimits())
101}