diff options
| author | 2026-03-28 04:19:44 +0000 | |
|---|---|---|
| committer | 2026-03-28 04:20:29 +0000 | |
| commit | 402ef2733813d128631ca4aea18c2908c74340d5 (patch) | |
| tree | e03a90b6f41411bd62e7339390802c5c50082850 /object/store/loose/read_test.go | |
| parent | object/store: Rename from object/storer (diff) | |
| signature | No signature | |
object/store: Rename back from storer; rename Store to ReadingStore v0.1.118
Diffstat (limited to 'object/store/loose/read_test.go')
| -rw-r--r-- | object/store/loose/read_test.go | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/object/store/loose/read_test.go b/object/store/loose/read_test.go new file mode 100644 index 00000000..fcb4fe17 --- /dev/null +++ b/object/store/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" + objectstore "codeberg.org/lindenii/furgit/object/store" + "codeberg.org/lindenii/furgit/object/store/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, objectstore.ErrObjectNotFound) { + t.Fatalf("ReadBytesFull not-found error = %v", err) + } + + _, _, err = store.ReadBytesContent(notFoundID) + if !errors.Is(err, objectstore.ErrObjectNotFound) { + t.Fatalf("ReadBytesContent not-found error = %v", err) + } + + _, err = store.ReadReaderFull(notFoundID) + if !errors.Is(err, objectstore.ErrObjectNotFound) { + t.Fatalf("ReadReaderFull not-found error = %v", err) + } + + _, _, _, err = store.ReadReaderContent(notFoundID) + if !errors.Is(err, objectstore.ErrObjectNotFound) { + t.Fatalf("ReadReaderContent not-found error = %v", err) + } + + _, _, err = store.ReadHeader(notFoundID) + if !errors.Is(err, objectstore.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") + } + }) +} |
