Fast implementation of Git in pure Go
1package commitquery_test
2
3import (
4 "errors"
5 "fmt"
6 "testing"
7
8 giterrors "codeberg.org/lindenii/furgit/errors"
9 "codeberg.org/lindenii/furgit/internal/testgit"
10 "codeberg.org/lindenii/furgit/object"
11 "codeberg.org/lindenii/furgit/objectid"
12 "codeberg.org/lindenii/furgit/objectstore/memory"
13 "codeberg.org/lindenii/furgit/objecttype"
14
15 "codeberg.org/lindenii/furgit/commitquery"
16)
17
18// ancestorCommitBody serializes one minimal commit body.
19func ancestorCommitBody(tree objectid.ObjectID, parents ...objectid.ObjectID) []byte {
20 buf := fmt.Appendf(nil, "tree %s\n", tree.String())
21 for _, parent := range parents {
22 buf = append(buf, fmt.Appendf(nil, "parent %s\n", parent.String())...)
23 }
24
25 buf = append(buf, []byte("\nmsg\n")...)
26
27 return buf
28}
29
30// ancestorTagBody serializes one minimal annotated tag body.
31func ancestorTagBody(target objectid.ObjectID, targetType objecttype.Type) []byte {
32 targetName, ok := objecttype.Name(targetType)
33 if !ok {
34 panic("invalid tag target type")
35 }
36
37 return fmt.Appendf(nil, "object %s\ntype %s\ntag t\n\nmsg\n", target.String(), targetName)
38}
39
40// mustSerializeAncestorTree serializes one tree or fails the test.
41func mustSerializeAncestorTree(tb testing.TB, tree *object.Tree) []byte {
42 tb.Helper()
43
44 body, err := tree.SerializeWithoutHeader()
45 if err != nil {
46 tb.Fatalf("SerializeWithoutHeader: %v", err)
47 }
48
49 return body
50}
51
52func TestIs(t *testing.T) {
53 t.Parallel()
54
55 testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
56 store := memory.New(algo)
57 blob := store.AddObject(objecttype.TypeBlob, []byte("blob\n"))
58 tree := store.AddObject(objecttype.TypeTree, mustSerializeAncestorTree(t, &object.Tree{Entries: []object.TreeEntry{{
59 Mode: object.FileModeRegular,
60 Name: []byte("f"),
61 ID: blob,
62 }}}))
63 c1 := store.AddObject(objecttype.TypeCommit, ancestorCommitBody(tree))
64 c2 := store.AddObject(objecttype.TypeCommit, ancestorCommitBody(tree, c1))
65 otherBlob := store.AddObject(objecttype.TypeBlob, []byte("other-blob\n"))
66 otherTree := store.AddObject(objecttype.TypeTree, mustSerializeAncestorTree(t, &object.Tree{Entries: []object.TreeEntry{{
67 Mode: object.FileModeRegular,
68 Name: []byte("g"),
69 ID: otherBlob,
70 }}}))
71 c3 := store.AddObject(objecttype.TypeCommit, ancestorCommitBody(otherTree))
72 tag := store.AddObject(objecttype.TypeTag, ancestorTagBody(c2, objecttype.TypeCommit))
73
74 ok, err := commitquery.New(store, nil).IsAncestor(c1, tag)
75 if err != nil {
76 t.Fatalf("Is(c1, tag): %v", err)
77 }
78
79 if !ok {
80 t.Fatal("expected c1 to be ancestor of tag->c2")
81 }
82
83 ok, err = commitquery.New(store, nil).IsAncestor(c3, c2)
84 if err != nil {
85 t.Fatalf("Is(c3, c2): %v", err)
86 }
87
88 if ok {
89 t.Fatal("did not expect c3 to be ancestor of c2")
90 }
91 })
92}
93
94func TestIsRejectsNonCommitAfterPeel(t *testing.T) {
95 t.Parallel()
96
97 testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
98 store := memory.New(algo)
99 blob := store.AddObject(objecttype.TypeBlob, []byte("blob\n"))
100 tree := store.AddObject(objecttype.TypeTree, mustSerializeAncestorTree(t, &object.Tree{Entries: []object.TreeEntry{{
101 Mode: object.FileModeRegular,
102 Name: []byte("f"),
103 ID: blob,
104 }}}))
105 commit := store.AddObject(objecttype.TypeCommit, ancestorCommitBody(tree))
106 tagToTree := store.AddObject(objecttype.TypeTag, ancestorTagBody(tree, objecttype.TypeTree))
107
108 _, err := commitquery.New(store, nil).IsAncestor(commit, tagToTree)
109 if err == nil {
110 t.Fatal("expected error")
111 }
112
113 if _, ok := errors.AsType[*giterrors.ObjectTypeError](err); !ok {
114 t.Fatalf("expected ObjectTypeError, got %T (%v)", err, err)
115 }
116 })
117}