package packp
import (
"bytes"
"io"
"strings"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/format/pktline"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
. "gopkg.in/check.v1"
)
type AdvRefsDecodeSuite struct{}
var _ = Suite(&AdvRefsDecodeSuite{})
func (s *AdvRefsDecodeSuite) TestEmpty(c *C) {
var buf bytes.Buffer
ar := NewAdvRefs()
c.Assert(ar.Decode(&buf), Equals, ErrEmptyInput)
}
func (s *AdvRefsDecodeSuite) TestEmptyFlush(c *C) {
var buf bytes.Buffer
e := pktline.NewEncoder(&buf)
e.Flush()
ar := NewAdvRefs()
c.Assert(ar.Decode(&buf), Equals, ErrEmptyAdvRefs)
}
func (s *AdvRefsDecodeSuite) TestEmptyPrefixFlush(c *C) {
var buf bytes.Buffer
e := pktline.NewEncoder(&buf)
e.EncodeString("# service=git-upload-pack")
e.Flush()
e.Flush()
ar := NewAdvRefs()
c.Assert(ar.Decode(&buf), Equals, ErrEmptyAdvRefs)
}
func (s *AdvRefsDecodeSuite) TestShortForHash(c *C) {
payloads := []string{
"6ecf0ef2c2dffb796",
pktline.FlushString,
}
r := toPktLines(c, payloads)
s.testDecoderErrorMatches(c, r, ".*too short.*")
}
func (s *AdvRefsDecodeSuite) testDecoderErrorMatches(c *C, input io.Reader, pattern string) {
ar := NewAdvRefs()
c.Assert(ar.Decode(input), ErrorMatches, pattern)
}
func (s *AdvRefsDecodeSuite) TestInvalidFirstHash(c *C) {
payloads := []string{
"6ecf0ef2c2dffb796alberto2219af86ec6584e5 HEAD\x00multi_ack thin-pack\n",
pktline.FlushString,
}
r := toPktLines(c, payloads)
s.testDecoderErrorMatches(c, r, ".*invalid hash.*")
}
func (s *AdvRefsDecodeSuite) TestZeroId(c *C) {
payloads := []string{
"0000000000000000000000000000000000000000 capabilities^{}\x00multi_ack thin-pack\n",
pktline.FlushString,
}
ar := s.testDecodeOK(c, payloads)
c.Assert(ar.Head, IsNil)
}
func (s *AdvRefsDecodeSuite) testDecodeOK(c *C, payloads []string) *AdvRefs {
var buf bytes.Buffer
e := pktline.NewEncoder(&buf)
err := e.EncodeString(payloads...)
c.Assert(err, IsNil)
ar := NewAdvRefs()
c.Assert(ar.Decode(&buf), IsNil)
return ar
}
func (s *AdvRefsDecodeSuite) TestMalformedZeroId(c *C) {
payloads := []string{
"0000000000000000000000000000000000000000 wrong\x00multi_ack thin-pack\n",
pktline.FlushString,
}
r := toPktLines(c, payloads)
s.testDecoderErrorMatches(c, r, ".*malformed zero-id.*")
}
func (s *AdvRefsDecodeSuite) TestShortZeroId(c *C) {
payloads := []string{
"0000000000000000000000000000000000000000 capabi",
pktline.FlushString,
}
r := toPktLines(c, payloads)
s.testDecoderErrorMatches(c, r, ".*too short zero-id.*")
}
func (s *AdvRefsDecodeSuite) TestHead(c *C) {
payloads := []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00",
pktline.FlushString,
}
ar := s.testDecodeOK(c, payloads)
c.Assert(*ar.Head, Equals,
plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
}
func (s *AdvRefsDecodeSuite) TestFirstIsNotHead(c *C) {
payloads := []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 refs/heads/master\x00",
pktline.FlushString,
}
ar := s.testDecodeOK(c, payloads)
c.Assert(ar.Head, IsNil)
c.Assert(ar.References["refs/heads/master"], Equals,
plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
}
func (s *AdvRefsDecodeSuite) TestShortRef(c *C) {
payloads := []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 H",
pktline.FlushString,
}
r := toPktLines(c, payloads)
s.testDecoderErrorMatches(c, r, ".*too short.*")
}
func (s *AdvRefsDecodeSuite) TestNoNULL(c *C) {
payloads := []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEADofs-delta multi_ack",
pktline.FlushString,
}
r := toPktLines(c, payloads)
s.testDecoderErrorMatches(c, r, ".*NULL not found.*")
}
func (s *AdvRefsDecodeSuite) TestNoSpaceAfterHash(c *C) {
payloads := []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5-HEAD\x00",
pktline.FlushString,
}
r := toPktLines(c, payloads)
s.testDecoderErrorMatches(c, r, ".*no space after hash.*")
}
func (s *AdvRefsDecodeSuite) TestNoCaps(c *C) {
payloads := []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00",
pktline.FlushString,
}
ar := s.testDecodeOK(c, payloads)
c.Assert(ar.Capabilities.IsEmpty(), Equals, true)
}
func (s *AdvRefsDecodeSuite) TestCaps(c *C) {
type entry struct {
Name capability.Capability
Values []string
}
for _, test := range [...]struct {
input []string
capabilities []entry
}{{
input: []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00",
pktline.FlushString,
},
capabilities: []entry{},
}, {
input: []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00\n",
pktline.FlushString,
},
capabilities: []entry{},
}, {
input: []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta",
pktline.FlushString,
},
capabilities: []entry{
{
Name: capability.OFSDelta,
Values: []string(nil),
},
},
}, {
input: []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta multi_ack",
pktline.FlushString,
},
capabilities: []entry{
{Name: capability.OFSDelta, Values: []string(nil)},
{Name: capability.MultiACK, Values: []string(nil)},
},
}, {
input: []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta multi_ack\n",
pktline.FlushString,
},
capabilities: []entry{
{Name: capability.OFSDelta, Values: []string(nil)},
{Name: capability.MultiACK, Values: []string(nil)},
},
}, {
input: []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00symref=HEAD:refs/heads/master agent=foo=bar\n",
pktline.FlushString,
},
capabilities: []entry{
{Name: capability.SymRef, Values: []string{"HEAD:refs/heads/master"}},
{Name: capability.Agent, Values: []string{"foo=bar"}},
},
}, {
input: []string{
"0000000000000000000000000000000000000000 capabilities^{}\x00report-status report-status-v2 delete-refs side-band-64k quiet atomic ofs-delta object-format=sha1 agent=git/2.41.0\n",
pktline.FlushString,
},
capabilities: []entry{
{Name: capability.ReportStatus, Values: []string(nil)},
{Name: capability.ObjectFormat, Values: []string{"sha1"}},
{Name: capability.Agent, Values: []string{"git/2.41.0"}},
},
}} {
ar := s.testDecodeOK(c, test.input)
for _, fixCap := range test.capabilities {
c.Assert(ar.Capabilities.Supports(fixCap.Name), Equals, true,
Commentf("input = %q, capability = %q", test.input, fixCap.Name))
c.Assert(ar.Capabilities.Get(fixCap.Name), DeepEquals, fixCap.Values,
Commentf("input = %q, capability = %q", test.input, fixCap.Name))
}
}
}
func (s *AdvRefsDecodeSuite) TestWithPrefix(c *C) {
payloads := []string{
"# this is a prefix\n",
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta\n",
pktline.FlushString,
}
ar := s.testDecodeOK(c, payloads)
c.Assert(len(ar.Prefix), Equals, 1)
c.Assert(ar.Prefix[0], DeepEquals, []byte("# this is a prefix"))
}
func (s *AdvRefsDecodeSuite) TestWithPrefixAndFlush(c *C) {
payloads := []string{
"# this is a prefix\n",
pktline.FlushString,
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta\n",
pktline.FlushString,
}
ar := s.testDecodeOK(c, payloads)
c.Assert(len(ar.Prefix), Equals, 2)
c.Assert(ar.Prefix[0], DeepEquals, []byte("# this is a prefix"))
c.Assert(ar.Prefix[1], DeepEquals, []byte(pktline.FlushString))
}
func (s *AdvRefsDecodeSuite) TestOtherRefs(c *C) {
for _, test := range [...]struct {
input []string
references map[string]plumbing.Hash
peeled map[string]plumbing.Hash
}{{
input: []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n",
pktline.FlushString,
},
references: make(map[string]plumbing.Hash),
peeled: make(map[string]plumbing.Hash),
}, {
input: []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n",
"1111111111111111111111111111111111111111 ref/foo",
pktline.FlushString,
},
references: map[string]plumbing.Hash{
"ref/foo": plumbing.NewHash("1111111111111111111111111111111111111111"),
},
peeled: make(map[string]plumbing.Hash),
}, {
input: []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n",
"1111111111111111111111111111111111111111 ref/foo\n",
pktline.FlushString,
},
references: map[string]plumbing.Hash{
"ref/foo": plumbing.NewHash("1111111111111111111111111111111111111111"),
},
peeled: make(map[string]plumbing.Hash),
}, {
input: []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n",
"1111111111111111111111111111111111111111 ref/foo\n",
"2222222222222222222222222222222222222222 ref/bar",
pktline.FlushString,
},
references: map[string]plumbing.Hash{
"ref/foo": plumbing.NewHash("1111111111111111111111111111111111111111"),
"ref/bar": plumbing.NewHash("2222222222222222222222222222222222222222"),
},
peeled: make(map[string]plumbing.Hash),
}, {
input: []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n",
"1111111111111111111111111111111111111111 ref/foo^{}\n",
pktline.FlushString,
},
references: make(map[string]plumbing.Hash),
peeled: map[string]plumbing.Hash{
"ref/foo": plumbing.NewHash("1111111111111111111111111111111111111111"),
},
}, {
input: []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n",
"1111111111111111111111111111111111111111 ref/foo\n",
"2222222222222222222222222222222222222222 ref/bar^{}",
pktline.FlushString,
},
references: map[string]plumbing.Hash{
"ref/foo": plumbing.NewHash("1111111111111111111111111111111111111111"),
},
peeled: map[string]plumbing.Hash{
"ref/bar": plumbing.NewHash("2222222222222222222222222222222222222222"),
},
}, {
input: []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n",
"a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n",
"51b8b4fb32271d39fbdd760397406177b2b0fd36 refs/pull/10/head\n",
"02b5a6031ba7a8cbfde5d65ff9e13ecdbc4a92ca refs/pull/100/head\n",
"c284c212704c43659bf5913656b8b28e32da1621 refs/pull/100/merge\n",
"3d6537dce68c8b7874333a1720958bd8db3ae8ca refs/pull/101/merge\n",
"5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11\n",
"c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11^{}\n",
"5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n",
"c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n",
pktline.FlushString,
},
references: map[string]plumbing.Hash{
"refs/heads/master": plumbing.NewHash("a6930aaee06755d1bdcfd943fbf614e4d92bb0c7"),
"refs/pull/10/head": plumbing.NewHash("51b8b4fb32271d39fbdd760397406177b2b0fd36"),
"refs/pull/100/head": plumbing.NewHash("02b5a6031ba7a8cbfde5d65ff9e13ecdbc4a92ca"),
"refs/pull/100/merge": plumbing.NewHash("c284c212704c43659bf5913656b8b28e32da1621"),
"refs/pull/101/merge": plumbing.NewHash("3d6537dce68c8b7874333a1720958bd8db3ae8ca"),
"refs/tags/v2.6.11": plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c"),
"refs/tags/v2.6.11-tree": plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c"),
},
peeled: map[string]plumbing.Hash{
"refs/tags/v2.6.11": plumbing.NewHash("c39ae07f393806ccf406ef966e9a15afc43cc36a"),
"refs/tags/v2.6.11-tree": plumbing.NewHash("c39ae07f393806ccf406ef966e9a15afc43cc36a"),
},
}} {
ar := s.testDecodeOK(c, test.input)
comment := Commentf("input = %v\n", test.input)
c.Assert(ar.References, DeepEquals, test.references, comment)
c.Assert(ar.Peeled, DeepEquals, test.peeled, comment)
}
}
func (s *AdvRefsDecodeSuite) TestMalformedOtherRefsNoSpace(c *C) {
payloads := []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00multi_ack thin-pack\n",
"5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8crefs/tags/v2.6.11\n",
pktline.FlushString,
}
r := toPktLines(c, payloads)
s.testDecoderErrorMatches(c, r, ".*malformed ref data.*")
}
func (s *AdvRefsDecodeSuite) TestMalformedOtherRefsMultipleSpaces(c *C) {
payloads := []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00multi_ack thin-pack\n",
"5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags v2.6.11\n",
pktline.FlushString,
}
r := toPktLines(c, payloads)
s.testDecoderErrorMatches(c, r, ".*malformed ref data.*")
}
func (s *AdvRefsDecodeSuite) TestShallow(c *C) {
for _, test := range [...]struct {
input []string
shallows []plumbing.Hash
}{{
input: []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n",
"a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n",
"5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n",
"c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n",
pktline.FlushString,
},
shallows: []plumbing.Hash{},
}, {
input: []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n",
"a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n",
"5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n",
"c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n",
"shallow 1111111111111111111111111111111111111111\n",
pktline.FlushString,
},
shallows: []plumbing.Hash{plumbing.NewHash("1111111111111111111111111111111111111111")},
}, {
input: []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n",
"a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n",
"5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n",
"c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n",
"shallow 1111111111111111111111111111111111111111\n",
"shallow 2222222222222222222222222222222222222222\n",
pktline.FlushString,
},
shallows: []plumbing.Hash{
plumbing.NewHash("1111111111111111111111111111111111111111"),
plumbing.NewHash("2222222222222222222222222222222222222222"),
},
}} {
ar := s.testDecodeOK(c, test.input)
comment := Commentf("input = %v\n", test.input)
c.Assert(ar.Shallows, DeepEquals, test.shallows, comment)
}
}
func (s *AdvRefsDecodeSuite) TestInvalidShallowHash(c *C) {
payloads := []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n",
"a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n",
"5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n",
"c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n",
"shallow 11111111alcortes111111111111111111111111\n",
"shallow 2222222222222222222222222222222222222222\n",
pktline.FlushString,
}
r := toPktLines(c, payloads)
s.testDecoderErrorMatches(c, r, ".*invalid hash text.*")
}
func (s *AdvRefsDecodeSuite) TestGarbageAfterShallow(c *C) {
payloads := []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n",
"a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n",
"5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n",
"c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n",
"shallow 1111111111111111111111111111111111111111\n",
"shallow 2222222222222222222222222222222222222222\n",
"b5be40b90dbaa6bd337f3b77de361bfc0723468b refs/tags/v4.4",
pktline.FlushString,
}
r := toPktLines(c, payloads)
s.testDecoderErrorMatches(c, r, ".*malformed shallow prefix.*")
}
func (s *AdvRefsDecodeSuite) TestMalformedShallowHash(c *C) {
payloads := []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n",
"a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n",
"5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n",
"c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n",
"shallow 1111111111111111111111111111111111111111\n",
"shallow 2222222222222222222222222222222222222222 malformed\n",
pktline.FlushString,
}
r := toPktLines(c, payloads)
s.testDecoderErrorMatches(c, r, ".*malformed shallow hash.*")
}
func (s *AdvRefsDecodeSuite) TestEOFRefs(c *C) {
input := strings.NewReader("" +
"005b6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n" +
"003fa6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n" +
"00355dc01c595e6c6ec9ccda4f6ffbf614e4d92bb0c7 refs/foo\n",
)
s.testDecoderErrorMatches(c, input, ".*invalid pkt-len.*")
}
func (s *AdvRefsDecodeSuite) TestEOFShallows(c *C) {
input := strings.NewReader("" +
"005b6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n" +
"003fa6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n" +
"00445dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n" +
"0047c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n" +
"0035shallow 1111111111111111111111111111111111111111\n" +
"0034shallow 222222222222222222222222")
s.testDecoderErrorMatches(c, input, ".*unexpected EOF.*")
}