package packrev_test
import (
"bytes"
"errors"
"os"
"testing"
"lindenii.org/go/furgit/internal/format/packidx"
"lindenii.org/go/furgit/internal/format/packrev"
"lindenii.org/go/furgit/object/id"
)
// writeSyntheticRev writes one reverse index over positions
// with a fixed fake pack hash.
func writeSyntheticRev(t *testing.T, objectFormat id.ObjectFormat, positions []uint32) []byte {
t.Helper()
packHash := bytes.Repeat([]byte{0x5a}, objectFormat.Size())
var buf bytes.Buffer
err := packrev.Write(&buf, objectFormat, positions, packHash)
if err != nil {
t.Fatalf("Write: %v", err)
}
return buf.Bytes()
}
func TestWriteRoundTrip(t *testing.T) {
t.Parallel()
for _, objectFormat := range id.SupportedObjectFormats() {
t.Run(objectFormat.String(), func(t *testing.T) {
t.Parallel()
positions := []uint32{8, 6, 7, 5, 3, 0, 4, 1, 2}
data := writeSyntheticRev(t, objectFormat, positions)
rev, err := packrev.Parse(data, objectFormat)
if err != nil {
t.Fatalf("Parse: %v", err)
}
if rev.NumObjects() != len(positions) {
t.Fatalf("NumObjects = %d, want %d", rev.NumObjects(), len(positions))
}
if !bytes.Equal(rev.PackHash(), bytes.Repeat([]byte{0x5a}, objectFormat.Size())) {
t.Fatalf("PackHash mismatch")
}
for packOrder, want := range positions {
position, err := rev.PositionAt(packOrder)
if err != nil {
t.Fatalf("PositionAt(%d): %v", packOrder, err)
}
if position != int(want) {
t.Fatalf("PositionAt(%d) = %d, want %d", packOrder, position, want)
}
}
})
}
}
func TestWriteMatchesGit(t *testing.T) {
t.Parallel()
for _, objectFormat := range id.SupportedObjectFormats() {
t.Run(objectFormat.String(), func(t *testing.T) {
t.Parallel()
prefix := makeGitPack(t, objectFormat)
gitData, err := os.ReadFile(prefix + ".rev") //nolint:gosec
if err != nil {
t.Fatalf("ReadFile: %v", err)
}
idxData, err := os.ReadFile(prefix + ".idx") //nolint:gosec
if err != nil {
t.Fatalf("ReadFile: %v", err)
}
idx, err := packidx.Parse(idxData, objectFormat.Size())
if err != nil {
t.Fatalf("packidx.Parse: %v", err)
}
positions := packOrderPositions(t, &idx)
var buf bytes.Buffer
err = packrev.Write(&buf, objectFormat, positions, idx.PackHash())
if err != nil {
t.Fatalf("Write: %v", err)
}
if !bytes.Equal(buf.Bytes(), gitData) {
t.Fatalf("Write output differs from git's reverse index (%d vs %d bytes)", buf.Len(), len(gitData))
}
})
}
}
func TestWriteInvalidPositions(t *testing.T) {
t.Parallel()
for _, objectFormat := range id.SupportedObjectFormats() {
t.Run(objectFormat.String(), func(t *testing.T) {
t.Parallel()
packHash := bytes.Repeat([]byte{0x5a}, objectFormat.Size())
err := packrev.Write(&bytes.Buffer{}, objectFormat, []uint32{0, 5}, packHash)
if !errors.Is(err, packrev.ErrInvalidPositions) {
t.Fatalf("Write error = %v, want ErrInvalidPositions", err)
}
})
}
}
func TestWriteBadPackHashPanics(t *testing.T) {
t.Parallel()
defer func() {
if recover() == nil {
t.Fatalf("Write with short pack hash: expected panic")
}
}()
_ = packrev.Write(&bytes.Buffer{}, id.ObjectFormatSHA256, nil, []byte{0x01})
}