Browse Source

all: unwrap `database.LFSStore` interface (#7692)

Joe Chen 11 months ago
parent
commit
3a5132b6f7

+ 4 - 1
internal/database/database.go

@@ -124,7 +124,6 @@ func NewConnection(w logger.Writer) (*gorm.DB, error) {
 
 
 	// Initialize stores, sorted in alphabetical order.
 	// Initialize stores, sorted in alphabetical order.
 	LoginSources = &loginSourcesStore{DB: db, files: sourceFiles}
 	LoginSources = &loginSourcesStore{DB: db, files: sourceFiles}
-	LFS = &lfsStore{DB: db}
 	Notices = NewNoticesStore(db)
 	Notices = NewNoticesStore(db)
 	Orgs = NewOrgsStore(db)
 	Orgs = NewOrgsStore(db)
 	Perms = NewPermsStore(db)
 	Perms = NewPermsStore(db)
@@ -163,3 +162,7 @@ func (db *DB) AccessTokens() *AccessTokensStore {
 func (db *DB) Actions() *ActionsStore {
 func (db *DB) Actions() *ActionsStore {
 	return newActionsStore(db.db)
 	return newActionsStore(db.db)
 }
 }
+
+func (db *DB) LFS() *LFSStore {
+	return newLFSStore(db.db)
+}

+ 21 - 27
internal/database/lfs.go

@@ -6,6 +6,7 @@ package database
 
 
 import (
 import (
 	"context"
 	"context"
+	"errors"
 	"fmt"
 	"fmt"
 	"time"
 	"time"
 
 
@@ -15,20 +16,6 @@ import (
 	"gogs.io/gogs/internal/lfsutil"
 	"gogs.io/gogs/internal/lfsutil"
 )
 )
 
 
-// LFSStore is the persistent interface for LFS objects.
-type LFSStore interface {
-	// CreateObject creates a LFS object record in database.
-	CreateObject(ctx context.Context, repoID int64, oid lfsutil.OID, size int64, storage lfsutil.Storage) error
-	// GetObjectByOID returns the LFS object with given OID. It returns
-	// ErrLFSObjectNotExist when not found.
-	GetObjectByOID(ctx context.Context, repoID int64, oid lfsutil.OID) (*LFSObject, error)
-	// GetObjectsByOIDs returns LFS objects found within "oids". The returned list
-	// could have less elements if some oids were not found.
-	GetObjectsByOIDs(ctx context.Context, repoID int64, oids ...lfsutil.OID) ([]*LFSObject, error)
-}
-
-var LFS LFSStore
-
 // LFSObject is the relation between an LFS object and a repository.
 // LFSObject is the relation between an LFS object and a repository.
 type LFSObject struct {
 type LFSObject struct {
 	RepoID    int64           `gorm:"primaryKey;auto_increment:false"`
 	RepoID    int64           `gorm:"primaryKey;auto_increment:false"`
@@ -38,20 +25,24 @@ type LFSObject struct {
 	CreatedAt time.Time       `gorm:"not null"`
 	CreatedAt time.Time       `gorm:"not null"`
 }
 }
 
 
-var _ LFSStore = (*lfsStore)(nil)
+// LFSStore is the storage layer for LFS objects.
+type LFSStore struct {
+	db *gorm.DB
+}
 
 
-type lfsStore struct {
-	*gorm.DB
+func newLFSStore(db *gorm.DB) *LFSStore {
+	return &LFSStore{db: db}
 }
 }
 
 
-func (s *lfsStore) CreateObject(ctx context.Context, repoID int64, oid lfsutil.OID, size int64, storage lfsutil.Storage) error {
+// CreateObject creates an LFS object record in database.
+func (s *LFSStore) CreateObject(ctx context.Context, repoID int64, oid lfsutil.OID, size int64, storage lfsutil.Storage) error {
 	object := &LFSObject{
 	object := &LFSObject{
 		RepoID:  repoID,
 		RepoID:  repoID,
 		OID:     oid,
 		OID:     oid,
 		Size:    size,
 		Size:    size,
 		Storage: storage,
 		Storage: storage,
 	}
 	}
-	return s.WithContext(ctx).Create(object).Error
+	return s.db.WithContext(ctx).Create(object).Error
 }
 }
 
 
 type ErrLFSObjectNotExist struct {
 type ErrLFSObjectNotExist struct {
@@ -59,8 +50,7 @@ type ErrLFSObjectNotExist struct {
 }
 }
 
 
 func IsErrLFSObjectNotExist(err error) bool {
 func IsErrLFSObjectNotExist(err error) bool {
-	_, ok := err.(ErrLFSObjectNotExist)
-	return ok
+	return errors.As(err, &ErrLFSObjectNotExist{})
 }
 }
 
 
 func (err ErrLFSObjectNotExist) Error() string {
 func (err ErrLFSObjectNotExist) Error() string {
@@ -71,11 +61,13 @@ func (ErrLFSObjectNotExist) NotFound() bool {
 	return true
 	return true
 }
 }
 
 
-func (s *lfsStore) GetObjectByOID(ctx context.Context, repoID int64, oid lfsutil.OID) (*LFSObject, error) {
+// GetObjectByOID returns the LFS object with given OID. It returns
+// ErrLFSObjectNotExist when not found.
+func (s *LFSStore) GetObjectByOID(ctx context.Context, repoID int64, oid lfsutil.OID) (*LFSObject, error) {
 	object := new(LFSObject)
 	object := new(LFSObject)
-	err := s.WithContext(ctx).Where("repo_id = ? AND oid = ?", repoID, oid).First(object).Error
+	err := s.db.WithContext(ctx).Where("repo_id = ? AND oid = ?", repoID, oid).First(object).Error
 	if err != nil {
 	if err != nil {
-		if err == gorm.ErrRecordNotFound {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
 			return nil, ErrLFSObjectNotExist{args: errutil.Args{"repoID": repoID, "oid": oid}}
 			return nil, ErrLFSObjectNotExist{args: errutil.Args{"repoID": repoID, "oid": oid}}
 		}
 		}
 		return nil, err
 		return nil, err
@@ -83,14 +75,16 @@ func (s *lfsStore) GetObjectByOID(ctx context.Context, repoID int64, oid lfsutil
 	return object, err
 	return object, err
 }
 }
 
 
-func (s *lfsStore) GetObjectsByOIDs(ctx context.Context, repoID int64, oids ...lfsutil.OID) ([]*LFSObject, error) {
+// GetObjectsByOIDs returns LFS objects found within "oids". The returned list
+// could have fewer elements if some oids were not found.
+func (s *LFSStore) GetObjectsByOIDs(ctx context.Context, repoID int64, oids ...lfsutil.OID) ([]*LFSObject, error) {
 	if len(oids) == 0 {
 	if len(oids) == 0 {
 		return []*LFSObject{}, nil
 		return []*LFSObject{}, nil
 	}
 	}
 
 
 	objects := make([]*LFSObject, 0, len(oids))
 	objects := make([]*LFSObject, 0, len(oids))
-	err := s.WithContext(ctx).Where("repo_id = ? AND oid IN (?)", repoID, oids).Find(&objects).Error
-	if err != nil && err != gorm.ErrRecordNotFound {
+	err := s.db.WithContext(ctx).Where("repo_id = ? AND oid IN (?)", repoID, oids).Find(&objects).Error
+	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
 		return nil, err
 		return nil, err
 	}
 	}
 	return objects, nil
 	return objects, nil

+ 19 - 19
internal/database/lfs_test.go

@@ -23,13 +23,13 @@ func TestLFS(t *testing.T) {
 	t.Parallel()
 	t.Parallel()
 
 
 	ctx := context.Background()
 	ctx := context.Background()
-	db := &lfsStore{
-		DB: newTestDB(t, "lfsStore"),
+	s := &LFSStore{
+		db: newTestDB(t, "LFSStore"),
 	}
 	}
 
 
 	for _, tc := range []struct {
 	for _, tc := range []struct {
 		name string
 		name string
-		test func(t *testing.T, ctx context.Context, db *lfsStore)
+		test func(t *testing.T, ctx context.Context, s *LFSStore)
 	}{
 	}{
 		{"CreateObject", lfsCreateObject},
 		{"CreateObject", lfsCreateObject},
 		{"GetObjectByOID", lfsGetObjectByOID},
 		{"GetObjectByOID", lfsGetObjectByOID},
@@ -37,10 +37,10 @@ func TestLFS(t *testing.T) {
 	} {
 	} {
 		t.Run(tc.name, func(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			t.Cleanup(func() {
 			t.Cleanup(func() {
-				err := clearTables(t, db.DB)
+				err := clearTables(t, s.db)
 				require.NoError(t, err)
 				require.NoError(t, err)
 			})
 			})
-			tc.test(t, ctx, db)
+			tc.test(t, ctx, s)
 		})
 		})
 		if t.Failed() {
 		if t.Failed() {
 			break
 			break
@@ -48,52 +48,52 @@ func TestLFS(t *testing.T) {
 	}
 	}
 }
 }
 
 
-func lfsCreateObject(t *testing.T, ctx context.Context, db *lfsStore) {
+func lfsCreateObject(t *testing.T, ctx context.Context, s *LFSStore) {
 	// Create first LFS object
 	// Create first LFS object
 	repoID := int64(1)
 	repoID := int64(1)
 	oid := lfsutil.OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f")
 	oid := lfsutil.OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f")
-	err := db.CreateObject(ctx, repoID, oid, 12, lfsutil.StorageLocal)
+	err := s.CreateObject(ctx, repoID, oid, 12, lfsutil.StorageLocal)
 	require.NoError(t, err)
 	require.NoError(t, err)
 
 
 	// Get it back and check the CreatedAt field
 	// Get it back and check the CreatedAt field
-	object, err := db.GetObjectByOID(ctx, repoID, oid)
+	object, err := s.GetObjectByOID(ctx, repoID, oid)
 	require.NoError(t, err)
 	require.NoError(t, err)
-	assert.Equal(t, db.NowFunc().Format(time.RFC3339), object.CreatedAt.UTC().Format(time.RFC3339))
+	assert.Equal(t, s.db.NowFunc().Format(time.RFC3339), object.CreatedAt.UTC().Format(time.RFC3339))
 
 
-	// Try create second LFS object with same oid should fail
-	err = db.CreateObject(ctx, repoID, oid, 12, lfsutil.StorageLocal)
+	// Try to create second LFS object with same oid should fail
+	err = s.CreateObject(ctx, repoID, oid, 12, lfsutil.StorageLocal)
 	assert.Error(t, err)
 	assert.Error(t, err)
 }
 }
 
 
-func lfsGetObjectByOID(t *testing.T, ctx context.Context, db *lfsStore) {
+func lfsGetObjectByOID(t *testing.T, ctx context.Context, s *LFSStore) {
 	// Create a LFS object
 	// Create a LFS object
 	repoID := int64(1)
 	repoID := int64(1)
 	oid := lfsutil.OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f")
 	oid := lfsutil.OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f")
-	err := db.CreateObject(ctx, repoID, oid, 12, lfsutil.StorageLocal)
+	err := s.CreateObject(ctx, repoID, oid, 12, lfsutil.StorageLocal)
 	require.NoError(t, err)
 	require.NoError(t, err)
 
 
 	// We should be able to get it back
 	// We should be able to get it back
-	_, err = db.GetObjectByOID(ctx, repoID, oid)
+	_, err = s.GetObjectByOID(ctx, repoID, oid)
 	require.NoError(t, err)
 	require.NoError(t, err)
 
 
 	// Try to get a non-existent object
 	// Try to get a non-existent object
-	_, err = db.GetObjectByOID(ctx, repoID, "bad_oid")
+	_, err = s.GetObjectByOID(ctx, repoID, "bad_oid")
 	expErr := ErrLFSObjectNotExist{args: errutil.Args{"repoID": repoID, "oid": lfsutil.OID("bad_oid")}}
 	expErr := ErrLFSObjectNotExist{args: errutil.Args{"repoID": repoID, "oid": lfsutil.OID("bad_oid")}}
 	assert.Equal(t, expErr, err)
 	assert.Equal(t, expErr, err)
 }
 }
 
 
-func lfsGetObjectsByOIDs(t *testing.T, ctx context.Context, db *lfsStore) {
+func lfsGetObjectsByOIDs(t *testing.T, ctx context.Context, s *LFSStore) {
 	// Create two LFS objects
 	// Create two LFS objects
 	repoID := int64(1)
 	repoID := int64(1)
 	oid1 := lfsutil.OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f")
 	oid1 := lfsutil.OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f")
 	oid2 := lfsutil.OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64g")
 	oid2 := lfsutil.OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64g")
-	err := db.CreateObject(ctx, repoID, oid1, 12, lfsutil.StorageLocal)
+	err := s.CreateObject(ctx, repoID, oid1, 12, lfsutil.StorageLocal)
 	require.NoError(t, err)
 	require.NoError(t, err)
-	err = db.CreateObject(ctx, repoID, oid2, 12, lfsutil.StorageLocal)
+	err = s.CreateObject(ctx, repoID, oid2, 12, lfsutil.StorageLocal)
 	require.NoError(t, err)
 	require.NoError(t, err)
 
 
 	// We should be able to get them back and ignore non-existent ones
 	// We should be able to get them back and ignore non-existent ones
-	objects, err := db.GetObjectsByOIDs(ctx, repoID, oid1, oid2, "bad_oid")
+	objects, err := s.GetObjectsByOIDs(ctx, repoID, oid1, oid2, "bad_oid")
 	require.NoError(t, err)
 	require.NoError(t, err)
 	assert.Equal(t, 2, len(objects), "number of objects")
 	assert.Equal(t, 2, len(objects), "number of objects")
 
 

+ 0 - 8
internal/database/mocks.go

@@ -8,14 +8,6 @@ import (
 	"testing"
 	"testing"
 )
 )
 
 
-func SetMockLFSStore(t *testing.T, mock LFSStore) {
-	before := LFS
-	LFS = mock
-	t.Cleanup(func() {
-		LFS = before
-	})
-}
-
 func setMockLoginSourcesStore(t *testing.T, mock LoginSourcesStore) {
 func setMockLoginSourcesStore(t *testing.T, mock LoginSourcesStore) {
 	before := LoginSources
 	before := LoginSources
 	LoginSources = mock
 	LoginSources = mock

+ 6 - 5
internal/route/lfs/basic.go

@@ -25,6 +25,7 @@ const (
 )
 )
 
 
 type basicHandler struct {
 type basicHandler struct {
+	store Store
 	// The default storage backend for uploading new objects.
 	// The default storage backend for uploading new objects.
 	defaultStorage lfsutil.Storage
 	defaultStorage lfsutil.Storage
 	// The list of available storage backends to access objects.
 	// The list of available storage backends to access objects.
@@ -43,7 +44,7 @@ func (h *basicHandler) Storager(storage lfsutil.Storage) lfsutil.Storager {
 
 
 // GET /{owner}/{repo}.git/info/lfs/object/basic/{oid}
 // GET /{owner}/{repo}.git/info/lfs/object/basic/{oid}
 func (h *basicHandler) serveDownload(c *macaron.Context, repo *database.Repository, oid lfsutil.OID) {
 func (h *basicHandler) serveDownload(c *macaron.Context, repo *database.Repository, oid lfsutil.OID) {
-	object, err := database.LFS.GetObjectByOID(c.Req.Context(), repo.ID, oid)
+	object, err := h.store.GetLFSObjectByOID(c.Req.Context(), repo.ID, oid)
 	if err != nil {
 	if err != nil {
 		if database.IsErrLFSObjectNotExist(err) {
 		if database.IsErrLFSObjectNotExist(err) {
 			responseJSON(c.Resp, http.StatusNotFound, responseError{
 			responseJSON(c.Resp, http.StatusNotFound, responseError{
@@ -78,7 +79,7 @@ func (h *basicHandler) serveDownload(c *macaron.Context, repo *database.Reposito
 func (h *basicHandler) serveUpload(c *macaron.Context, repo *database.Repository, oid lfsutil.OID) {
 func (h *basicHandler) serveUpload(c *macaron.Context, repo *database.Repository, oid lfsutil.OID) {
 	// NOTE: LFS client will retry upload the same object if there was a partial failure,
 	// NOTE: LFS client will retry upload the same object if there was a partial failure,
 	// therefore we would like to skip ones that already exist.
 	// therefore we would like to skip ones that already exist.
-	_, err := database.LFS.GetObjectByOID(c.Req.Context(), repo.ID, oid)
+	_, err := h.store.GetLFSObjectByOID(c.Req.Context(), repo.ID, oid)
 	if err == nil {
 	if err == nil {
 		// Object exists, drain the request body and we're good.
 		// Object exists, drain the request body and we're good.
 		_, _ = io.Copy(io.Discard, c.Req.Request.Body)
 		_, _ = io.Copy(io.Discard, c.Req.Request.Body)
@@ -105,7 +106,7 @@ func (h *basicHandler) serveUpload(c *macaron.Context, repo *database.Repository
 		return
 		return
 	}
 	}
 
 
-	err = database.LFS.CreateObject(c.Req.Context(), repo.ID, oid, written, s.Storage())
+	err = h.store.CreateLFSObject(c.Req.Context(), repo.ID, oid, written, s.Storage())
 	if err != nil {
 	if err != nil {
 		// NOTE: It is OK to leave the file when the whole operation failed
 		// NOTE: It is OK to leave the file when the whole operation failed
 		// with a DB error, a retry on client side can safely overwrite the
 		// with a DB error, a retry on client side can safely overwrite the
@@ -120,7 +121,7 @@ func (h *basicHandler) serveUpload(c *macaron.Context, repo *database.Repository
 }
 }
 
 
 // POST /{owner}/{repo}.git/info/lfs/object/basic/verify
 // POST /{owner}/{repo}.git/info/lfs/object/basic/verify
-func (*basicHandler) serveVerify(c *macaron.Context, repo *database.Repository) {
+func (h *basicHandler) serveVerify(c *macaron.Context, repo *database.Repository) {
 	var request basicVerifyRequest
 	var request basicVerifyRequest
 	defer func() { _ = c.Req.Request.Body.Close() }()
 	defer func() { _ = c.Req.Request.Body.Close() }()
 
 
@@ -139,7 +140,7 @@ func (*basicHandler) serveVerify(c *macaron.Context, repo *database.Repository)
 		return
 		return
 	}
 	}
 
 
-	object, err := database.LFS.GetObjectByOID(c.Req.Context(), repo.ID, request.Oid)
+	object, err := h.store.GetLFSObjectByOID(c.Req.Context(), repo.ID, request.Oid)
 	if err != nil {
 	if err != nil {
 		if database.IsErrLFSObjectNotExist(err) {
 		if database.IsErrLFSObjectNotExist(err) {
 			responseJSON(c.Resp, http.StatusNotFound, responseError{
 			responseJSON(c.Resp, http.StatusNotFound, responseError{

+ 55 - 64
internal/route/lfs/basic_test.go

@@ -13,6 +13,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 	"gopkg.in/macaron.v1"
 	"gopkg.in/macaron.v1"
 
 
 	"gogs.io/gogs/internal/database"
 	"gogs.io/gogs/internal/database"
@@ -21,7 +22,7 @@ import (
 
 
 var _ lfsutil.Storager = (*mockStorage)(nil)
 var _ lfsutil.Storager = (*mockStorage)(nil)
 
 
-// mockStorage is a in-memory storage for LFS objects.
+// mockStorage is an in-memory storage for LFS objects.
 type mockStorage struct {
 type mockStorage struct {
 	buf *bytes.Buffer
 	buf *bytes.Buffer
 }
 }
@@ -31,7 +32,7 @@ func (*mockStorage) Storage() lfsutil.Storage {
 }
 }
 
 
 func (s *mockStorage) Upload(_ lfsutil.OID, rc io.ReadCloser) (int64, error) {
 func (s *mockStorage) Upload(_ lfsutil.OID, rc io.ReadCloser) (int64, error) {
-	defer rc.Close()
+	defer func() { _ = rc.Close() }()
 	return io.Copy(s.buf, rc)
 	return io.Copy(s.buf, rc)
 }
 }
 
 
@@ -40,7 +41,7 @@ func (s *mockStorage) Download(_ lfsutil.OID, w io.Writer) error {
 	return err
 	return err
 }
 }
 
 
-func Test_basicHandler_serveDownload(t *testing.T) {
+func TestBasicHandler_serveDownload(t *testing.T) {
 	s := &mockStorage{}
 	s := &mockStorage{}
 	basic := &basicHandler{
 	basic := &basicHandler{
 		defaultStorage: s.Storage(),
 		defaultStorage: s.Storage(),
@@ -60,17 +61,17 @@ func Test_basicHandler_serveDownload(t *testing.T) {
 	tests := []struct {
 	tests := []struct {
 		name          string
 		name          string
 		content       string
 		content       string
-		mockLFSStore  func() database.LFSStore
+		mockStore     func() *MockStore
 		expStatusCode int
 		expStatusCode int
 		expHeader     http.Header
 		expHeader     http.Header
 		expBody       string
 		expBody       string
 	}{
 	}{
 		{
 		{
 			name: "object does not exist",
 			name: "object does not exist",
-			mockLFSStore: func() database.LFSStore {
-				mock := NewMockLFSStore()
-				mock.GetObjectByOIDFunc.SetDefaultReturn(nil, database.ErrLFSObjectNotExist{})
-				return mock
+			mockStore: func() *MockStore {
+				mockStore := NewMockStore()
+				mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(nil, database.ErrLFSObjectNotExist{})
+				return mockStore
 			},
 			},
 			expStatusCode: http.StatusNotFound,
 			expStatusCode: http.StatusNotFound,
 			expHeader: http.Header{
 			expHeader: http.Header{
@@ -80,10 +81,10 @@ func Test_basicHandler_serveDownload(t *testing.T) {
 		},
 		},
 		{
 		{
 			name: "storage not found",
 			name: "storage not found",
-			mockLFSStore: func() database.LFSStore {
-				mock := NewMockLFSStore()
-				mock.GetObjectByOIDFunc.SetDefaultReturn(&database.LFSObject{Storage: "bad_storage"}, nil)
-				return mock
+			mockStore: func() *MockStore {
+				mockStore := NewMockStore()
+				mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(&database.LFSObject{Storage: "bad_storage"}, nil)
+				return mockStore
 			},
 			},
 			expStatusCode: http.StatusInternalServerError,
 			expStatusCode: http.StatusInternalServerError,
 			expHeader: http.Header{
 			expHeader: http.Header{
@@ -95,16 +96,16 @@ func Test_basicHandler_serveDownload(t *testing.T) {
 		{
 		{
 			name:    "object exists",
 			name:    "object exists",
 			content: "Hello world!",
 			content: "Hello world!",
-			mockLFSStore: func() database.LFSStore {
-				mock := NewMockLFSStore()
-				mock.GetObjectByOIDFunc.SetDefaultReturn(
+			mockStore: func() *MockStore {
+				mockStore := NewMockStore()
+				mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(
 					&database.LFSObject{
 					&database.LFSObject{
 						Size:    12,
 						Size:    12,
 						Storage: s.Storage(),
 						Storage: s.Storage(),
 					},
 					},
 					nil,
 					nil,
 				)
 				)
-				return mock
+				return mockStore
 			},
 			},
 			expStatusCode: http.StatusOK,
 			expStatusCode: http.StatusOK,
 			expHeader: http.Header{
 			expHeader: http.Header{
@@ -116,14 +117,12 @@ func Test_basicHandler_serveDownload(t *testing.T) {
 	}
 	}
 	for _, test := range tests {
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
 		t.Run(test.name, func(t *testing.T) {
-			database.SetMockLFSStore(t, test.mockLFSStore())
+			basic.store = test.mockStore()
 
 
 			s.buf = bytes.NewBufferString(test.content)
 			s.buf = bytes.NewBufferString(test.content)
 
 
-			r, err := http.NewRequest("GET", "/", nil)
-			if err != nil {
-				t.Fatal(err)
-			}
+			r, err := http.NewRequest(http.MethodGet, "/", nil)
+			require.NoError(t, err)
 
 
 			rr := httptest.NewRecorder()
 			rr := httptest.NewRecorder()
 			m.ServeHTTP(rr, r)
 			m.ServeHTTP(rr, r)
@@ -133,15 +132,13 @@ func Test_basicHandler_serveDownload(t *testing.T) {
 			assert.Equal(t, test.expHeader, resp.Header)
 			assert.Equal(t, test.expHeader, resp.Header)
 
 
 			body, err := io.ReadAll(resp.Body)
 			body, err := io.ReadAll(resp.Body)
-			if err != nil {
-				t.Fatal(err)
-			}
+			require.NoError(t, err)
 			assert.Equal(t, test.expBody, string(body))
 			assert.Equal(t, test.expBody, string(body))
 		})
 		})
 	}
 	}
 }
 }
 
 
-func Test_basicHandler_serveUpload(t *testing.T) {
+func TestBasicHandler_serveUpload(t *testing.T) {
 	s := &mockStorage{buf: &bytes.Buffer{}}
 	s := &mockStorage{buf: &bytes.Buffer{}}
 	basic := &basicHandler{
 	basic := &basicHandler{
 		defaultStorage: s.Storage(),
 		defaultStorage: s.Storage(),
@@ -160,37 +157,35 @@ func Test_basicHandler_serveUpload(t *testing.T) {
 
 
 	tests := []struct {
 	tests := []struct {
 		name          string
 		name          string
-		mockLFSStore  func() database.LFSStore
+		mockStore     func() *MockStore
 		expStatusCode int
 		expStatusCode int
 		expBody       string
 		expBody       string
 	}{
 	}{
 		{
 		{
 			name: "object already exists",
 			name: "object already exists",
-			mockLFSStore: func() database.LFSStore {
-				mock := NewMockLFSStore()
-				mock.GetObjectByOIDFunc.SetDefaultReturn(&database.LFSObject{}, nil)
-				return mock
+			mockStore: func() *MockStore {
+				mockStore := NewMockStore()
+				mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(&database.LFSObject{}, nil)
+				return mockStore
 			},
 			},
 			expStatusCode: http.StatusOK,
 			expStatusCode: http.StatusOK,
 		},
 		},
 		{
 		{
 			name: "new object",
 			name: "new object",
-			mockLFSStore: func() database.LFSStore {
-				mock := NewMockLFSStore()
-				mock.GetObjectByOIDFunc.SetDefaultReturn(nil, database.ErrLFSObjectNotExist{})
-				return mock
+			mockStore: func() *MockStore {
+				mockStore := NewMockStore()
+				mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(nil, database.ErrLFSObjectNotExist{})
+				return mockStore
 			},
 			},
 			expStatusCode: http.StatusOK,
 			expStatusCode: http.StatusOK,
 		},
 		},
 	}
 	}
 	for _, test := range tests {
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
 		t.Run(test.name, func(t *testing.T) {
-			database.SetMockLFSStore(t, test.mockLFSStore())
+			basic.store = test.mockStore()
 
 
 			r, err := http.NewRequest("PUT", "/", strings.NewReader("Hello world!"))
 			r, err := http.NewRequest("PUT", "/", strings.NewReader("Hello world!"))
-			if err != nil {
-				t.Fatal(err)
-			}
+			require.NoError(t, err)
 
 
 			rr := httptest.NewRecorder()
 			rr := httptest.NewRecorder()
 			m.ServeHTTP(rr, r)
 			m.ServeHTTP(rr, r)
@@ -199,26 +194,26 @@ func Test_basicHandler_serveUpload(t *testing.T) {
 			assert.Equal(t, test.expStatusCode, resp.StatusCode)
 			assert.Equal(t, test.expStatusCode, resp.StatusCode)
 
 
 			body, err := io.ReadAll(resp.Body)
 			body, err := io.ReadAll(resp.Body)
-			if err != nil {
-				t.Fatal(err)
-			}
+			require.NoError(t, err)
 			assert.Equal(t, test.expBody, string(body))
 			assert.Equal(t, test.expBody, string(body))
 		})
 		})
 	}
 	}
 }
 }
 
 
-func Test_basicHandler_serveVerify(t *testing.T) {
+func TestBasicHandler_serveVerify(t *testing.T) {
+	basic := &basicHandler{}
+
 	m := macaron.New()
 	m := macaron.New()
 	m.Use(macaron.Renderer())
 	m.Use(macaron.Renderer())
 	m.Use(func(c *macaron.Context) {
 	m.Use(func(c *macaron.Context) {
 		c.Map(&database.Repository{Name: "repo"})
 		c.Map(&database.Repository{Name: "repo"})
 	})
 	})
-	m.Post("/", (&basicHandler{}).serveVerify)
+	m.Post("/", basic.serveVerify)
 
 
 	tests := []struct {
 	tests := []struct {
 		name          string
 		name          string
 		body          string
 		body          string
-		mockLFSStore  func() database.LFSStore
+		mockStore     func() *MockStore
 		expStatusCode int
 		expStatusCode int
 		expBody       string
 		expBody       string
 	}{
 	}{
@@ -231,10 +226,10 @@ func Test_basicHandler_serveVerify(t *testing.T) {
 		{
 		{
 			name: "object does not exist",
 			name: "object does not exist",
 			body: `{"oid":"ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f"}`,
 			body: `{"oid":"ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f"}`,
-			mockLFSStore: func() database.LFSStore {
-				mock := NewMockLFSStore()
-				mock.GetObjectByOIDFunc.SetDefaultReturn(nil, database.ErrLFSObjectNotExist{})
-				return mock
+			mockStore: func() *MockStore {
+				mockStore := NewMockStore()
+				mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(nil, database.ErrLFSObjectNotExist{})
+				return mockStore
 			},
 			},
 			expStatusCode: http.StatusNotFound,
 			expStatusCode: http.StatusNotFound,
 			expBody:       `{"message":"Object does not exist"}` + "\n",
 			expBody:       `{"message":"Object does not exist"}` + "\n",
@@ -242,10 +237,10 @@ func Test_basicHandler_serveVerify(t *testing.T) {
 		{
 		{
 			name: "object size mismatch",
 			name: "object size mismatch",
 			body: `{"oid":"ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f"}`,
 			body: `{"oid":"ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f"}`,
-			mockLFSStore: func() database.LFSStore {
-				mock := NewMockLFSStore()
-				mock.GetObjectByOIDFunc.SetDefaultReturn(&database.LFSObject{Size: 12}, nil)
-				return mock
+			mockStore: func() *MockStore {
+				mockStore := NewMockStore()
+				mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(&database.LFSObject{Size: 12}, nil)
+				return mockStore
 			},
 			},
 			expStatusCode: http.StatusBadRequest,
 			expStatusCode: http.StatusBadRequest,
 			expBody:       `{"message":"Object size mismatch"}` + "\n",
 			expBody:       `{"message":"Object size mismatch"}` + "\n",
@@ -254,24 +249,22 @@ func Test_basicHandler_serveVerify(t *testing.T) {
 		{
 		{
 			name: "object exists",
 			name: "object exists",
 			body: `{"oid":"ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f", "size":12}`,
 			body: `{"oid":"ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f", "size":12}`,
-			mockLFSStore: func() database.LFSStore {
-				mock := NewMockLFSStore()
-				mock.GetObjectByOIDFunc.SetDefaultReturn(&database.LFSObject{Size: 12}, nil)
-				return mock
+			mockStore: func() *MockStore {
+				mockStore := NewMockStore()
+				mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(&database.LFSObject{Size: 12}, nil)
+				return mockStore
 			},
 			},
 			expStatusCode: http.StatusOK,
 			expStatusCode: http.StatusOK,
 		},
 		},
 	}
 	}
 	for _, test := range tests {
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
 		t.Run(test.name, func(t *testing.T) {
-			if test.mockLFSStore != nil {
-				database.SetMockLFSStore(t, test.mockLFSStore())
+			if test.mockStore != nil {
+				basic.store = test.mockStore()
 			}
 			}
 
 
 			r, err := http.NewRequest("POST", "/", strings.NewReader(test.body))
 			r, err := http.NewRequest("POST", "/", strings.NewReader(test.body))
-			if err != nil {
-				t.Fatal(err)
-			}
+			require.NoError(t, err)
 
 
 			rr := httptest.NewRecorder()
 			rr := httptest.NewRecorder()
 			m.ServeHTTP(rr, r)
 			m.ServeHTTP(rr, r)
@@ -280,9 +273,7 @@ func Test_basicHandler_serveVerify(t *testing.T) {
 			assert.Equal(t, test.expStatusCode, resp.StatusCode)
 			assert.Equal(t, test.expStatusCode, resp.StatusCode)
 
 
 			body, err := io.ReadAll(resp.Body)
 			body, err := io.ReadAll(resp.Body)
-			if err != nil {
-				t.Fatal(err)
-			}
+			require.NoError(t, err)
 			assert.Equal(t, test.expBody, string(body))
 			assert.Equal(t, test.expBody, string(body))
 		})
 		})
 	}
 	}

+ 90 - 88
internal/route/lfs/batch.go

@@ -19,111 +19,113 @@ import (
 )
 )
 
 
 // POST /{owner}/{repo}.git/info/lfs/object/batch
 // POST /{owner}/{repo}.git/info/lfs/object/batch
-func serveBatch(c *macaron.Context, owner *database.User, repo *database.Repository) {
-	var request batchRequest
-	defer c.Req.Request.Body.Close()
-	err := jsoniter.NewDecoder(c.Req.Request.Body).Decode(&request)
-	if err != nil {
-		responseJSON(c.Resp, http.StatusBadRequest, responseError{
-			Message: strutil.ToUpperFirst(err.Error()),
-		})
-		return
-	}
+func serveBatch(store Store) macaron.Handler {
+	return func(c *macaron.Context, owner *database.User, repo *database.Repository) {
+		var request batchRequest
+		defer func() { _ = c.Req.Request.Body.Close() }()
+		err := jsoniter.NewDecoder(c.Req.Request.Body).Decode(&request)
+		if err != nil {
+			responseJSON(c.Resp, http.StatusBadRequest, responseError{
+				Message: strutil.ToUpperFirst(err.Error()),
+			})
+			return
+		}
 
 
-	// NOTE: We only support basic transfer as of now.
-	transfer := transferBasic
-	// Example: https://try.gogs.io/gogs/gogs.git/info/lfs/object/basic
-	baseHref := fmt.Sprintf("%s%s/%s.git/info/lfs/objects/basic", conf.Server.ExternalURL, owner.Name, repo.Name)
-
-	objects := make([]batchObject, 0, len(request.Objects))
-	switch request.Operation {
-	case basicOperationUpload:
-		for _, obj := range request.Objects {
-			var actions batchActions
-			if lfsutil.ValidOID(obj.Oid) {
-				actions = batchActions{
-					Upload: &batchAction{
-						Href: fmt.Sprintf("%s/%s", baseHref, obj.Oid),
-						Header: map[string]string{
-							// NOTE: git-lfs v2.5.0 sets the Content-Type based on the uploaded file.
-							// This ensures that the client always uses the designated value for the header.
-							"Content-Type": "application/octet-stream",
+		// NOTE: We only support basic transfer as of now.
+		transfer := transferBasic
+		// Example: https://try.gogs.io/gogs/gogs.git/info/lfs/object/basic
+		baseHref := fmt.Sprintf("%s%s/%s.git/info/lfs/objects/basic", conf.Server.ExternalURL, owner.Name, repo.Name)
+
+		objects := make([]batchObject, 0, len(request.Objects))
+		switch request.Operation {
+		case basicOperationUpload:
+			for _, obj := range request.Objects {
+				var actions batchActions
+				if lfsutil.ValidOID(obj.Oid) {
+					actions = batchActions{
+						Upload: &batchAction{
+							Href: fmt.Sprintf("%s/%s", baseHref, obj.Oid),
+							Header: map[string]string{
+								// NOTE: git-lfs v2.5.0 sets the Content-Type based on the uploaded file.
+								// This ensures that the client always uses the designated value for the header.
+								"Content-Type": "application/octet-stream",
+							},
 						},
 						},
-					},
-					Verify: &batchAction{
-						Href: fmt.Sprintf("%s/verify", baseHref),
-					},
-				}
-			} else {
-				actions = batchActions{
-					Error: &batchError{
-						Code:    http.StatusUnprocessableEntity,
-						Message: "Object has invalid oid",
-					},
+						Verify: &batchAction{
+							Href: fmt.Sprintf("%s/verify", baseHref),
+						},
+					}
+				} else {
+					actions = batchActions{
+						Error: &batchError{
+							Code:    http.StatusUnprocessableEntity,
+							Message: "Object has invalid oid",
+						},
+					}
 				}
 				}
-			}
 
 
-			objects = append(objects, batchObject{
-				Oid:     obj.Oid,
-				Size:    obj.Size,
-				Actions: actions,
-			})
-		}
+				objects = append(objects, batchObject{
+					Oid:     obj.Oid,
+					Size:    obj.Size,
+					Actions: actions,
+				})
+			}
 
 
-	case basicOperationDownload:
-		oids := make([]lfsutil.OID, 0, len(request.Objects))
-		for _, obj := range request.Objects {
-			oids = append(oids, obj.Oid)
-		}
-		stored, err := database.LFS.GetObjectsByOIDs(c.Req.Context(), repo.ID, oids...)
-		if err != nil {
-			internalServerError(c.Resp)
-			log.Error("Failed to get objects [repo_id: %d, oids: %v]: %v", repo.ID, oids, err)
-			return
-		}
-		storedSet := make(map[lfsutil.OID]*database.LFSObject, len(stored))
-		for _, obj := range stored {
-			storedSet[obj.OID] = obj
-		}
+		case basicOperationDownload:
+			oids := make([]lfsutil.OID, 0, len(request.Objects))
+			for _, obj := range request.Objects {
+				oids = append(oids, obj.Oid)
+			}
+			stored, err := store.GetLFSObjectsByOIDs(c.Req.Context(), repo.ID, oids...)
+			if err != nil {
+				internalServerError(c.Resp)
+				log.Error("Failed to get objects [repo_id: %d, oids: %v]: %v", repo.ID, oids, err)
+				return
+			}
+			storedSet := make(map[lfsutil.OID]*database.LFSObject, len(stored))
+			for _, obj := range stored {
+				storedSet[obj.OID] = obj
+			}
 
 
-		for _, obj := range request.Objects {
-			var actions batchActions
-			if stored := storedSet[obj.Oid]; stored != nil {
-				if stored.Size != obj.Size {
-					actions.Error = &batchError{
-						Code:    http.StatusUnprocessableEntity,
-						Message: "Object size mismatch",
+			for _, obj := range request.Objects {
+				var actions batchActions
+				if stored := storedSet[obj.Oid]; stored != nil {
+					if stored.Size != obj.Size {
+						actions.Error = &batchError{
+							Code:    http.StatusUnprocessableEntity,
+							Message: "Object size mismatch",
+						}
+					} else {
+						actions.Download = &batchAction{
+							Href: fmt.Sprintf("%s/%s", baseHref, obj.Oid),
+						}
 					}
 					}
 				} else {
 				} else {
-					actions.Download = &batchAction{
-						Href: fmt.Sprintf("%s/%s", baseHref, obj.Oid),
+					actions.Error = &batchError{
+						Code:    http.StatusNotFound,
+						Message: "Object does not exist",
 					}
 					}
 				}
 				}
-			} else {
-				actions.Error = &batchError{
-					Code:    http.StatusNotFound,
-					Message: "Object does not exist",
-				}
+
+				objects = append(objects, batchObject{
+					Oid:     obj.Oid,
+					Size:    obj.Size,
+					Actions: actions,
+				})
 			}
 			}
 
 
-			objects = append(objects, batchObject{
-				Oid:     obj.Oid,
-				Size:    obj.Size,
-				Actions: actions,
+		default:
+			responseJSON(c.Resp, http.StatusBadRequest, responseError{
+				Message: "Operation not recognized",
 			})
 			})
+			return
 		}
 		}
 
 
-	default:
-		responseJSON(c.Resp, http.StatusBadRequest, responseError{
-			Message: "Operation not recognized",
+		responseJSON(c.Resp, http.StatusOK, batchResponse{
+			Transfer: transfer,
+			Objects:  objects,
 		})
 		})
-		return
 	}
 	}
-
-	responseJSON(c.Resp, http.StatusOK, batchResponse{
-		Transfer: transfer,
-		Objects:  objects,
-	})
 }
 }
 
 
 // batchRequest defines the request payload for the batch endpoint.
 // batchRequest defines the request payload for the batch endpoint.

+ 22 - 28
internal/route/lfs/batch_test.go

@@ -13,28 +13,22 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 	"gopkg.in/macaron.v1"
 	"gopkg.in/macaron.v1"
 
 
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/database"
 	"gogs.io/gogs/internal/database"
 )
 )
 
 
-func Test_serveBatch(t *testing.T) {
+func TestServeBatch(t *testing.T) {
 	conf.SetMockServer(t, conf.ServerOpts{
 	conf.SetMockServer(t, conf.ServerOpts{
 		ExternalURL: "https://gogs.example.com/",
 		ExternalURL: "https://gogs.example.com/",
 	})
 	})
 
 
-	m := macaron.New()
-	m.Use(func(c *macaron.Context) {
-		c.Map(&database.User{Name: "owner"})
-		c.Map(&database.Repository{Name: "repo"})
-	})
-	m.Post("/", serveBatch)
-
 	tests := []struct {
 	tests := []struct {
 		name          string
 		name          string
 		body          string
 		body          string
-		mockLFSStore  func() database.LFSStore
+		mockStore     func() *MockStore
 		expStatusCode int
 		expStatusCode int
 		expBody       string
 		expBody       string
 	}{
 	}{
@@ -82,9 +76,9 @@ func Test_serveBatch(t *testing.T) {
 	{"oid": "ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f", "size": 123},
 	{"oid": "ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f", "size": 123},
 	{"oid": "5cac0a318669fadfee734fb340a5f5b70b428ac57a9f4b109cb6e150b2ba7e57", "size": 456}
 	{"oid": "5cac0a318669fadfee734fb340a5f5b70b428ac57a9f4b109cb6e150b2ba7e57", "size": 456}
 ]}`,
 ]}`,
-			mockLFSStore: func() database.LFSStore {
-				mock := NewMockLFSStore()
-				mock.GetObjectsByOIDsFunc.SetDefaultReturn(
+			mockStore: func() *MockStore {
+				mockStore := NewMockStore()
+				mockStore.GetLFSObjectsByOIDsFunc.SetDefaultReturn(
 					[]*database.LFSObject{
 					[]*database.LFSObject{
 						{
 						{
 							OID:  "ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f",
 							OID:  "ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f",
@@ -96,7 +90,7 @@ func Test_serveBatch(t *testing.T) {
 					},
 					},
 					nil,
 					nil,
 				)
 				)
-				return mock
+				return mockStore
 			},
 			},
 			expStatusCode: http.StatusOK,
 			expStatusCode: http.StatusOK,
 			expBody: `{
 			expBody: `{
@@ -123,14 +117,20 @@ func Test_serveBatch(t *testing.T) {
 	}
 	}
 	for _, test := range tests {
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
 		t.Run(test.name, func(t *testing.T) {
-			if test.mockLFSStore != nil {
-				database.SetMockLFSStore(t, test.mockLFSStore())
+			mockStore := NewMockStore()
+			if test.mockStore != nil {
+				mockStore = test.mockStore()
 			}
 			}
 
 
-			r, err := http.NewRequest("POST", "/", bytes.NewBufferString(test.body))
-			if err != nil {
-				t.Fatal(err)
-			}
+			m := macaron.New()
+			m.Use(func(c *macaron.Context) {
+				c.Map(&database.User{Name: "owner"})
+				c.Map(&database.Repository{Name: "repo"})
+			})
+			m.Post("/", serveBatch(mockStore))
+
+			r, err := http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(test.body))
+			require.NoError(t, err)
 
 
 			rr := httptest.NewRecorder()
 			rr := httptest.NewRecorder()
 			m.ServeHTTP(rr, r)
 			m.ServeHTTP(rr, r)
@@ -139,21 +139,15 @@ func Test_serveBatch(t *testing.T) {
 			assert.Equal(t, test.expStatusCode, resp.StatusCode)
 			assert.Equal(t, test.expStatusCode, resp.StatusCode)
 
 
 			body, err := io.ReadAll(resp.Body)
 			body, err := io.ReadAll(resp.Body)
-			if err != nil {
-				t.Fatal(err)
-			}
+			require.NoError(t, err)
 
 
 			var expBody bytes.Buffer
 			var expBody bytes.Buffer
 			err = json.Indent(&expBody, []byte(test.expBody), "", "  ")
 			err = json.Indent(&expBody, []byte(test.expBody), "", "  ")
-			if err != nil {
-				t.Fatal(err)
-			}
+			require.NoError(t, err)
 
 
 			var gotBody bytes.Buffer
 			var gotBody bytes.Buffer
 			err = json.Indent(&gotBody, body, "", "  ")
 			err = json.Indent(&gotBody, body, "", "  ")
-			if err != nil {
-				t.Fatal(err)
-			}
+			require.NoError(t, err)
 
 
 			assert.Equal(t, expBody.String(), gotBody.String())
 			assert.Equal(t, expBody.String(), gotBody.String())
 		})
 		})

File diff suppressed because it is too large
+ 391 - 288
internal/route/lfs/mocks_test.go


+ 2 - 1
internal/route/lfs/route.go

@@ -28,9 +28,10 @@ func RegisterRoutes(r *macaron.Router) {
 
 
 	store := NewStore()
 	store := NewStore()
 	r.Group("", func() {
 	r.Group("", func() {
-		r.Post("/objects/batch", authorize(database.AccessModeRead), verifyAccept, verifyContentTypeJSON, serveBatch)
+		r.Post("/objects/batch", authorize(database.AccessModeRead), verifyAccept, verifyContentTypeJSON, serveBatch(store))
 		r.Group("/objects/basic", func() {
 		r.Group("/objects/basic", func() {
 			basic := &basicHandler{
 			basic := &basicHandler{
+				store:          store,
 				defaultStorage: lfsutil.Storage(conf.LFS.Storage),
 				defaultStorage: lfsutil.Storage(conf.LFS.Storage),
 				storagers: map[lfsutil.Storage]lfsutil.Storager{
 				storagers: map[lfsutil.Storage]lfsutil.Storager{
 					lfsutil.StorageLocal: &lfsutil.LocalStorage{Root: conf.LFS.ObjectsPath},
 					lfsutil.StorageLocal: &lfsutil.LocalStorage{Root: conf.LFS.ObjectsPath},

+ 22 - 0
internal/route/lfs/store.go

@@ -4,6 +4,7 @@ import (
 	"context"
 	"context"
 
 
 	"gogs.io/gogs/internal/database"
 	"gogs.io/gogs/internal/database"
+	"gogs.io/gogs/internal/lfsutil"
 )
 )
 
 
 // Store is the data layer carrier for LFS endpoints. This interface is meant to
 // Store is the data layer carrier for LFS endpoints. This interface is meant to
@@ -16,6 +17,15 @@ type Store interface {
 	// TouchAccessTokenByID updates the updated time of the given access token to
 	// TouchAccessTokenByID updates the updated time of the given access token to
 	// the current time.
 	// the current time.
 	TouchAccessTokenByID(ctx context.Context, id int64) error
 	TouchAccessTokenByID(ctx context.Context, id int64) error
+
+	// CreateLFSObject creates an LFS object record in database.
+	CreateLFSObject(ctx context.Context, repoID int64, oid lfsutil.OID, size int64, storage lfsutil.Storage) error
+	// GetLFSObjectByOID returns the LFS object with given OID. It returns
+	// database.ErrLFSObjectNotExist when not found.
+	GetLFSObjectByOID(ctx context.Context, repoID int64, oid lfsutil.OID) (*database.LFSObject, error)
+	// GetLFSObjectsByOIDs returns LFS objects found within "oids". The returned
+	// list could have fewer elements if some oids were not found.
+	GetLFSObjectsByOIDs(ctx context.Context, repoID int64, oids ...lfsutil.OID) ([]*database.LFSObject, error)
 }
 }
 
 
 type store struct{}
 type store struct{}
@@ -32,3 +42,15 @@ func (*store) GetAccessTokenBySHA1(ctx context.Context, sha1 string) (*database.
 func (*store) TouchAccessTokenByID(ctx context.Context, id int64) error {
 func (*store) TouchAccessTokenByID(ctx context.Context, id int64) error {
 	return database.Handle.AccessTokens().Touch(ctx, id)
 	return database.Handle.AccessTokens().Touch(ctx, id)
 }
 }
+
+func (*store) CreateLFSObject(ctx context.Context, repoID int64, oid lfsutil.OID, size int64, storage lfsutil.Storage) error {
+	return database.Handle.LFS().CreateObject(ctx, repoID, oid, size, storage)
+}
+
+func (*store) GetLFSObjectByOID(ctx context.Context, repoID int64, oid lfsutil.OID) (*database.LFSObject, error) {
+	return database.Handle.LFS().GetObjectByOID(ctx, repoID, oid)
+}
+
+func (*store) GetLFSObjectsByOIDs(ctx context.Context, repoID int64, oids ...lfsutil.OID) ([]*database.LFSObject, error) {
+	return database.Handle.LFS().GetObjectsByOIDs(ctx, repoID, oids...)
+}

+ 0 - 1
mockgen.yaml

@@ -36,7 +36,6 @@ mocks:
     sources:
     sources:
       - path: gogs.io/gogs/internal/database
       - path: gogs.io/gogs/internal/database
         interfaces:
         interfaces:
-          - LFSStore
           - UsersStore
           - UsersStore
           - TwoFactorsStore
           - TwoFactorsStore
           - ReposStore
           - ReposStore

Some files were not shown because too many files changed in this diff