package loose_test import ( "bytes" "errors" "testing" "lindenii.org/go/furgit/internal/testgit" "lindenii.org/go/furgit/object/header" "lindenii.org/go/furgit/object/id" "lindenii.org/go/furgit/object/store" "lindenii.org/go/furgit/object/store/loose" "lindenii.org/go/furgit/object/typ" ) func TestWrite(t *testing.T) { t.Parallel() writes := []struct { name string write func(looseStore *loose.Loose, content []byte) (id.ObjectID, error) }{ { name: "BytesContent", write: func(looseStore *loose.Loose, content []byte) (id.ObjectID, error) { return looseStore.WriteBytesContent(typ.TypeBlob, content) }, }, { name: "ReaderContent", write: func(looseStore *loose.Loose, content []byte) (id.ObjectID, error) { return looseStore.WriteReaderContent(typ.TypeBlob, uint64(len(content)), bytes.NewReader(content)) }, }, { name: "BytesFull", write: func(looseStore *loose.Loose, content []byte) (id.ObjectID, error) { raw := header.Append(nil, typ.TypeBlob, uint64(len(content))) raw = append(raw, content...) return looseStore.WriteBytesFull(raw) }, }, { name: "ReaderFull", write: func(looseStore *loose.Loose, content []byte) (id.ObjectID, error) { raw := header.Append(nil, typ.TypeBlob, uint64(len(content))) raw = append(raw, content...) return looseStore.WriteReaderFull(bytes.NewReader(raw)) }, }, } for _, objectFormat := range id.SupportedObjectFormats() { t.Run(objectFormat.String(), func(t *testing.T) { t.Parallel() repo, err := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: objectFormat}) if err != nil { t.Fatalf("NewRepo: %v", err) } looseStore := openLooseStore(t, repo) for _, w := range writes { t.Run(w.name, func(t *testing.T) { content := []byte("written via " + w.name + "\n") want, err := repo.HashObject(t, typ.TypeBlob, bytes.NewReader(content)) if err != nil { t.Fatalf("HashObject: %v", err) } got, err := w.write(looseStore, content) if err != nil { t.Fatalf("write: %v", err) } if got != want { t.Fatalf("id = %s, want %s", got, want) } gotBody, err := repo.CatFile(t, typ.TypeBlob, got) if err != nil { t.Fatalf("CatFile: %v", err) } if !bytes.Equal(gotBody, content) { t.Fatalf("git cat-file body mismatch") } regot, err := w.write(looseStore, content) if err != nil { t.Fatalf("rewrite: %v", err) } if regot != want { t.Fatalf("rewrite id = %s, want %s", regot, want) } }) } }) } } func TestWriteRejects(t *testing.T) { t.Parallel() for _, objectFormat := range id.SupportedObjectFormats() { t.Run(objectFormat.String(), func(t *testing.T) { t.Parallel() repo, err := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: objectFormat}) if err != nil { t.Fatalf("NewRepo: %v", err) } looseStore := openLooseStore(t, repo) t.Run("ContentOverflow", func(t *testing.T) { t.Parallel() _, err := looseStore.WriteReaderContent(typ.TypeBlob, 1, bytes.NewReader([]byte("hello"))) if !errors.Is(err, store.ErrInvalidObject) { t.Fatalf("err = %v, want ErrInvalidObject", err) } }) t.Run("ContentShort", func(t *testing.T) { t.Parallel() _, err := looseStore.WriteReaderContent(typ.TypeBlob, 5, bytes.NewReader([]byte("x"))) if !errors.Is(err, store.ErrInvalidObject) { t.Fatalf("err = %v, want ErrInvalidObject", err) } }) t.Run("FullMalformedHeader", func(t *testing.T) { t.Parallel() _, err := looseStore.WriteReaderFull(bytes.NewReader([]byte("not-a-header"))) if !errors.Is(err, store.ErrInvalidObject) { t.Fatalf("err = %v, want ErrInvalidObject", err) } }) t.Run("FullSizeMismatch", func(t *testing.T) { t.Parallel() _, err := looseStore.WriteReaderFull(bytes.NewReader([]byte("blob 1\x00hello"))) if !errors.Is(err, store.ErrInvalidObject) { t.Fatalf("err = %v, want ErrInvalidObject", err) } }) }) } }