Sfoglia il codice sorgente

lfs: add tests to batch endpoint (#6073)

ᴜɴᴋɴᴡᴏɴ 4 anni fa
parent
commit
2b3655fa11
4 ha cambiato i file con 195 aggiunte e 40 eliminazioni
  1. 17 0
      internal/conf/mocks.go
  2. 42 40
      internal/conf/static.go
  3. 31 0
      internal/db/mocks.go
  4. 105 0
      internal/route/lfs/batch_test.go

+ 17 - 0
internal/conf/mocks.go

@@ -0,0 +1,17 @@
+// Copyright 2020 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package conf
+
+import (
+	"testing"
+)
+
+func SetMockServer(t *testing.T, opts ServerOpts) {
+	before := Server
+	Server = opts
+	t.Cleanup(func() {
+		Server = before
+	})
+}

+ 42 - 40
internal/conf/static.go

@@ -46,40 +46,6 @@ var (
 		AppName string
 	}
 
-	// Server settings
-	Server struct {
-		ExternalURL          string `ini:"EXTERNAL_URL"`
-		Domain               string
-		Protocol             string
-		HTTPAddr             string `ini:"HTTP_ADDR"`
-		HTTPPort             string `ini:"HTTP_PORT"`
-		CertFile             string
-		KeyFile              string
-		TLSMinVersion        string `ini:"TLS_MIN_VERSION"`
-		UnixSocketPermission string
-		LocalRootURL         string `ini:"LOCAL_ROOT_URL"`
-
-		OfflineMode      bool
-		DisableRouterLog bool
-		EnableGzip       bool
-
-		AppDataPath        string
-		LoadAssetsFromDisk bool
-
-		LandingURL string `ini:"LANDING_URL"`
-
-		// Derived from other static values
-		URL            *url.URL    `ini:"-"` // Parsed URL object of ExternalURL.
-		Subpath        string      `ini:"-"` // Subpath found the ExternalURL. Should be empty when not found.
-		SubpathDepth   int         `ini:"-"` // The number of slashes found in the Subpath.
-		UnixSocketMode os.FileMode `ini:"-"` // Parsed file mode of UnixSocketPermission.
-
-		// Deprecated: Use ExternalURL instead, will be removed in 0.13.
-		RootURL string `ini:"ROOT_URL"`
-		// Deprecated: Use LandingURL instead, will be removed in 0.13.
-		LangdingPage string `ini:"LANDING_PAGE"`
-	}
-
 	// SSH settings
 	SSH struct {
 		Disabled                     bool   `ini:"DISABLE_SSH"`
@@ -127,9 +93,6 @@ var (
 		} `ini:"repository.upload"`
 	}
 
-	// Database settings
-	Database DatabaseOpts
-
 	// Security settings
 	Security struct {
 		InstallLock             bool
@@ -279,9 +242,6 @@ var (
 		DefaultInterval int
 	}
 
-	// I18n settings
-	I18n *i18nConf
-
 	// Webhook settings
 	Webhook struct {
 		Types          []string
@@ -401,6 +361,42 @@ var (
 	HasRobotsTxt bool
 )
 
+type ServerOpts struct {
+	ExternalURL          string `ini:"EXTERNAL_URL"`
+	Domain               string
+	Protocol             string
+	HTTPAddr             string `ini:"HTTP_ADDR"`
+	HTTPPort             string `ini:"HTTP_PORT"`
+	CertFile             string
+	KeyFile              string
+	TLSMinVersion        string `ini:"TLS_MIN_VERSION"`
+	UnixSocketPermission string
+	LocalRootURL         string `ini:"LOCAL_ROOT_URL"`
+
+	OfflineMode      bool
+	DisableRouterLog bool
+	EnableGzip       bool
+
+	AppDataPath        string
+	LoadAssetsFromDisk bool
+
+	LandingURL string `ini:"LANDING_URL"`
+
+	// Derived from other static values
+	URL            *url.URL    `ini:"-"` // Parsed URL object of ExternalURL.
+	Subpath        string      `ini:"-"` // Subpath found the ExternalURL. Should be empty when not found.
+	SubpathDepth   int         `ini:"-"` // The number of slashes found in the Subpath.
+	UnixSocketMode os.FileMode `ini:"-"` // Parsed file mode of UnixSocketPermission.
+
+	// Deprecated: Use ExternalURL instead, will be removed in 0.13.
+	RootURL string `ini:"ROOT_URL"`
+	// Deprecated: Use LandingURL instead, will be removed in 0.13.
+	LangdingPage string `ini:"LANDING_PAGE"`
+}
+
+// Server settings
+var Server ServerOpts
+
 type DatabaseOpts struct {
 	Type         string
 	Host         string
@@ -418,6 +414,9 @@ type DatabaseOpts struct {
 	Passwd string
 }
 
+// Database settings
+var Database DatabaseOpts
+
 type i18nConf struct {
 	Langs     []string          `delim:","`
 	Names     []string          `delim:","`
@@ -433,6 +432,9 @@ func (c *i18nConf) DateLang(lang string) string {
 	return "en"
 }
 
+// I18n settings
+var I18n *i18nConf
+
 // handleDeprecated transfers deprecated values to the new ones when set.
 func handleDeprecated() {
 	if App.AppName != "" {

+ 31 - 0
internal/db/mocks.go

@@ -5,7 +5,10 @@
 package db
 
 import (
+	"io"
 	"testing"
+
+	"gogs.io/gogs/internal/lfsutil"
 )
 
 // NOTE: Mocks are sorted in alphabetical order.
@@ -33,6 +36,34 @@ func SetMockAccessTokensStore(t *testing.T, mock AccessTokensStore) {
 	})
 }
 
+var _ LFSStore = (*MockLFSStore)(nil)
+
+type MockLFSStore struct {
+	MockCreateObject     func(repoID int64, oid lfsutil.OID, rc io.ReadCloser, storage lfsutil.Storage) error
+	MockGetObjectByOID   func(repoID int64, oid lfsutil.OID) (*LFSObject, error)
+	MockGetObjectsByOIDs func(repoID int64, oids ...lfsutil.OID) ([]*LFSObject, error)
+}
+
+func (m *MockLFSStore) CreateObject(repoID int64, oid lfsutil.OID, rc io.ReadCloser, storage lfsutil.Storage) error {
+	return m.MockCreateObject(repoID, oid, rc, storage)
+}
+
+func (m *MockLFSStore) GetObjectByOID(repoID int64, oid lfsutil.OID) (*LFSObject, error) {
+	return m.MockGetObjectByOID(repoID, oid)
+}
+
+func (m *MockLFSStore) GetObjectsByOIDs(repoID int64, oids ...lfsutil.OID) ([]*LFSObject, error) {
+	return m.MockGetObjectsByOIDs(repoID, oids...)
+}
+
+func SetMockLFSStore(t *testing.T, mock LFSStore) {
+	before := LFS
+	LFS = mock
+	t.Cleanup(func() {
+		LFS = before
+	})
+}
+
 var _ PermsStore = (*MockPermsStore)(nil)
 
 type MockPermsStore struct {

+ 105 - 0
internal/route/lfs/batch_test.go

@@ -0,0 +1,105 @@
+// Copyright 2020 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package lfs
+
+import (
+	"bytes"
+	"io/ioutil"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"gopkg.in/macaron.v1"
+
+	"gogs.io/gogs/internal/conf"
+	"gogs.io/gogs/internal/db"
+	"gogs.io/gogs/internal/lfsutil"
+)
+
+func Test_serveBatch(t *testing.T) {
+	conf.SetMockServer(t, conf.ServerOpts{
+		ExternalURL: "https://gogs.example.com/",
+	})
+	m := macaron.New()
+	m.Use(func(c *macaron.Context) {
+		c.Map(&db.User{Name: "owner"})
+		c.Map(&db.Repository{Name: "repo"})
+	})
+	m.Post("/", serveBatch)
+
+	tests := []struct {
+		name          string
+		body          string
+		mockLFSStore  *db.MockLFSStore
+		expStatusCode int
+		expBody       string
+	}{
+		{
+			name:          "unrecognized operation",
+			body:          `{"operation": "update"}`,
+			expStatusCode: http.StatusBadRequest,
+			expBody:       `{"message":"Operation not recognized"}` + "\n",
+		},
+		{
+			name: "upload: contains invalid oid",
+			body: `{
+"operation": "upload",
+"objects": [
+	{"oid": "bad_oid", "size": 123},
+	{"oid": "ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f", "size": 123}
+]}`,
+			expStatusCode: http.StatusOK,
+			expBody:       `{"transfer":"basic","objects":[{"oid":"bad_oid","size":123,"actions":{"error":{"code":422,"message":"Object has invalid oid"}}},{"oid":"ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f","size":123,"actions":{"upload":{"href":"https://gogs.example.com/owner/repo.git/info/lfs/objects/basic/ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f"},"verify":{"href":"https://gogs.example.com/owner/repo.git/info/lfs/objects/basic/verify"}}}]}` + "\n",
+		},
+		{
+			name: "download: contains non-existent oid and mismatched size",
+			body: `{
+"operation": "download",
+"objects": [
+	{"oid": "bad_oid", "size": 123},
+	{"oid": "ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f", "size": 123},
+	{"oid": "5cac0a318669fadfee734fb340a5f5b70b428ac57a9f4b109cb6e150b2ba7e57", "size": 456}
+]}`,
+			mockLFSStore: &db.MockLFSStore{
+				MockGetObjectsByOIDs: func(repoID int64, oids ...lfsutil.OID) ([]*db.LFSObject, error) {
+					return []*db.LFSObject{
+						{
+							OID:  "ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f",
+							Size: 1234,
+						}, {
+							OID:  "5cac0a318669fadfee734fb340a5f5b70b428ac57a9f4b109cb6e150b2ba7e57",
+							Size: 456,
+						},
+					}, nil
+				},
+			},
+			expStatusCode: http.StatusOK,
+			expBody:       `{"transfer":"basic","objects":[{"oid":"bad_oid","size":123,"actions":{"error":{"code":404,"message":"Object does not exist"}}},{"oid":"ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f","size":123,"actions":{"error":{"code":422,"message":"Object size mismatch"}}},{"oid":"5cac0a318669fadfee734fb340a5f5b70b428ac57a9f4b109cb6e150b2ba7e57","size":456,"actions":{"download":{"href":"https://gogs.example.com/owner/repo.git/info/lfs/objects/basic/5cac0a318669fadfee734fb340a5f5b70b428ac57a9f4b109cb6e150b2ba7e57"}}}]}` + "\n",
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			db.SetMockLFSStore(t, test.mockLFSStore)
+
+			r, err := http.NewRequest("POST", "/", bytes.NewBufferString(test.body))
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			rr := httptest.NewRecorder()
+			m.ServeHTTP(rr, r)
+
+			resp := rr.Result()
+			assert.Equal(t, test.expStatusCode, resp.StatusCode)
+
+			body, err := ioutil.ReadAll(resp.Body)
+			if err != nil {
+				t.Fatal(err)
+			}
+			assert.Equal(t, test.expBody, string(body))
+		})
+	}
+}