aboutsummaryrefslogtreecommitdiff
path: root/object/storer/loose/read_test.go
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-03-25 14:34:50 +0000
committerGravatar Runxi Yu2026-03-25 14:34:50 +0000
commite4a7aa0742f5070299d37e8421c99d67f0af3f90 (patch)
tree36d89781476a92e61280c5ff232a2773e4092c0e /object/storer/loose/read_test.go
parent*: delta -> packfile/delta (diff)
signatureNo signature
*: object/store -> object/storer v0.1.107
Diffstat (limited to 'object/storer/loose/read_test.go')
-rw-r--r--object/storer/loose/read_test.go212
1 files changed, 212 insertions, 0 deletions
diff --git a/object/storer/loose/read_test.go b/object/storer/loose/read_test.go
new file mode 100644
index 00000000..ece3c9db
--- /dev/null
+++ b/object/storer/loose/read_test.go
@@ -0,0 +1,212 @@
+package loose_test
+
+import (
+ "bytes"
+ "errors"
+ "os"
+ "strings"
+ "testing"
+
+ "codeberg.org/lindenii/furgit/internal/testgit"
+ objectid "codeberg.org/lindenii/furgit/object/id"
+ "codeberg.org/lindenii/furgit/object/storer"
+ "codeberg.org/lindenii/furgit/object/storer/loose"
+ objecttype "codeberg.org/lindenii/furgit/object/type"
+)
+
+func TestLooseStoreReadAgainstGit(t *testing.T) {
+ t.Parallel()
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
+ testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo, Bare: true})
+ blobID := testRepo.HashObject(t, "blob", []byte("blob body\n"))
+ _, treeID, commitID := testRepo.MakeCommit(t, "subject\n\nbody")
+ tagID := testRepo.TagAnnotated(t, "v1", commitID, "tag message")
+
+ store := openLooseStore(t, testRepo, algo)
+
+ tests := []struct {
+ name string
+ id objectid.ObjectID
+ }{
+ {name: "blob", id: blobID},
+ {name: "tree", id: treeID},
+ {name: "commit", id: commitID},
+ {name: "tag", id: tagID},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ wantType, wantBody, wantRaw := expectedRawObject(t, testRepo, tt.id)
+
+ gotRaw, err := store.ReadBytesFull(tt.id)
+ if err != nil {
+ t.Fatalf("ReadBytesFull: %v", err)
+ }
+
+ if !bytes.Equal(gotRaw, wantRaw) {
+ t.Fatalf("ReadBytesFull mismatch")
+ }
+
+ gotType, gotBody, err := store.ReadBytesContent(tt.id)
+ if err != nil {
+ t.Fatalf("ReadBytesContent: %v", err)
+ }
+
+ if gotType != wantType {
+ t.Fatalf("ReadBytesContent type = %v, want %v", gotType, wantType)
+ }
+
+ if !bytes.Equal(gotBody, wantBody) {
+ t.Fatalf("ReadBytesContent body mismatch")
+ }
+
+ headType, headSize, err := store.ReadHeader(tt.id)
+ if err != nil {
+ t.Fatalf("ReadHeader: %v", err)
+ }
+
+ if headType != wantType {
+ t.Fatalf("ReadHeader type = %v, want %v", headType, wantType)
+ }
+
+ if headSize != int64(len(wantBody)) {
+ t.Fatalf("ReadHeader size = %d, want %d", headSize, len(wantBody))
+ }
+
+ fullReader, err := store.ReadReaderFull(tt.id)
+ if err != nil {
+ t.Fatalf("ReadReaderFull: %v", err)
+ }
+
+ got := mustReadAllAndClose(t, fullReader)
+ if !bytes.Equal(got, wantRaw) {
+ t.Fatalf("ReadReaderFull stream mismatch")
+ }
+
+ contentType, contentSize, contentReader, err := store.ReadReaderContent(tt.id)
+ if err != nil {
+ t.Fatalf("ReadReaderContent: %v", err)
+ }
+
+ if contentType != wantType {
+ t.Fatalf("ReadReaderContent type = %v, want %v", contentType, wantType)
+ }
+
+ if contentSize != int64(len(wantBody)) {
+ t.Fatalf("ReadReaderContent size = %d, want %d", contentSize, len(wantBody))
+ }
+
+ got = mustReadAllAndClose(t, contentReader)
+ if !bytes.Equal(got, wantBody) {
+ t.Fatalf("ReadReaderContent stream mismatch")
+ }
+ })
+ }
+ })
+}
+
+func TestLooseStoreErrors(t *testing.T) {
+ t.Parallel()
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
+ testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo, Bare: true})
+ store := openLooseStore(t, testRepo, algo)
+
+ notFoundID, err := objectid.ParseHex(algo, strings.Repeat("0", algo.HexLen()))
+ if err != nil {
+ t.Fatalf("ParseHex(notFoundID): %v", err)
+ }
+
+ _, err = store.ReadBytesFull(notFoundID)
+ if !errors.Is(err, objectstorer.ErrObjectNotFound) {
+ t.Fatalf("ReadBytesFull not-found error = %v", err)
+ }
+
+ _, _, err = store.ReadBytesContent(notFoundID)
+ if !errors.Is(err, objectstorer.ErrObjectNotFound) {
+ t.Fatalf("ReadBytesContent not-found error = %v", err)
+ }
+
+ _, err = store.ReadReaderFull(notFoundID)
+ if !errors.Is(err, objectstorer.ErrObjectNotFound) {
+ t.Fatalf("ReadReaderFull not-found error = %v", err)
+ }
+
+ _, _, _, err = store.ReadReaderContent(notFoundID)
+ if !errors.Is(err, objectstorer.ErrObjectNotFound) {
+ t.Fatalf("ReadReaderContent not-found error = %v", err)
+ }
+
+ _, _, err = store.ReadHeader(notFoundID)
+ if !errors.Is(err, objectstorer.ErrObjectNotFound) {
+ t.Fatalf("ReadHeader not-found error = %v", err)
+ }
+
+ var otherAlgo objectid.Algorithm
+ if algo == objectid.AlgorithmSHA1 {
+ otherAlgo = objectid.AlgorithmSHA256
+ } else {
+ otherAlgo = objectid.AlgorithmSHA1
+ }
+
+ otherID, err := objectid.ParseHex(otherAlgo, strings.Repeat("1", otherAlgo.HexLen()))
+ if err != nil {
+ t.Fatalf("ParseHex(otherID): %v", err)
+ }
+
+ _, err = store.ReadBytesFull(otherID)
+ if err == nil || !strings.Contains(err.Error(), "algorithm mismatch") {
+ t.Fatalf("ReadBytesFull algorithm-mismatch error = %v", err)
+ }
+ })
+}
+
+func TestLooseStoreNewValidation(t *testing.T) {
+ t.Parallel()
+
+ root, err := os.OpenRoot(t.TempDir())
+ if err != nil {
+ t.Fatalf("OpenRoot: %v", err)
+ }
+
+ defer func() { _ = root.Close() }()
+
+ _, err = loose.New(root, objectid.AlgorithmUnknown)
+ if err == nil {
+ t.Fatalf("loose.New(root, unknown) expected error")
+ }
+}
+
+func TestLooseStoreReadHeaderDoesNotVerifyAdler32(t *testing.T) {
+ t.Parallel()
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
+ testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo, Bare: true})
+ store := openLooseStore(t, testRepo, algo)
+
+ content := []byte("header-only-check\n")
+
+ id, err := store.WriteBytesContent(objecttype.TypeBlob, content)
+ if err != nil {
+ t.Fatalf("WriteBytesContent: %v", err)
+ }
+
+ corruptLooseObjectTrailer(t, testRepo, id)
+
+ ty, size, err := store.ReadHeader(id)
+ if err != nil {
+ t.Fatalf("ReadHeader: %v", err)
+ }
+
+ if ty != objecttype.TypeBlob {
+ t.Fatalf("ReadHeader type = %v, want %v", ty, objecttype.TypeBlob)
+ }
+
+ if size != int64(len(content)) {
+ t.Fatalf("ReadHeader size = %d, want %d", size, len(content))
+ }
+
+ _, err = store.ReadBytesFull(id)
+ if err == nil {
+ t.Fatalf("ReadBytesFull on corrupted trailer succeeded")
+ }
+ })
+}