gogs/internal/route/lfs/basic_test.go

281 lines
7.1 KiB
Go

// 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"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/macaron.v1"
"gogs.io/gogs/internal/database"
"gogs.io/gogs/internal/lfsutil"
)
var _ lfsutil.Storager = (*mockStorage)(nil)
// mockStorage is an in-memory storage for LFS objects.
type mockStorage struct {
buf *bytes.Buffer
}
func (*mockStorage) Storage() lfsutil.Storage {
return "memory"
}
func (s *mockStorage) Upload(_ lfsutil.OID, rc io.ReadCloser) (int64, error) {
defer func() { _ = rc.Close() }()
return io.Copy(s.buf, rc)
}
func (s *mockStorage) Download(_ lfsutil.OID, w io.Writer) error {
_, err := io.Copy(w, s.buf)
return err
}
func TestBasicHandler_serveDownload(t *testing.T) {
s := &mockStorage{}
basic := &basicHandler{
defaultStorage: s.Storage(),
storagers: map[lfsutil.Storage]lfsutil.Storager{
s.Storage(): s,
},
}
m := macaron.New()
m.Use(macaron.Renderer())
m.Use(func(c *macaron.Context) {
c.Map(&database.Repository{Name: "repo"})
c.Map(lfsutil.OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f"))
})
m.Get("/", basic.serveDownload)
tests := []struct {
name string
content string
mockStore func() *MockStore
expStatusCode int
expHeader http.Header
expBody string
}{
{
name: "object does not exist",
mockStore: func() *MockStore {
mockStore := NewMockStore()
mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(nil, database.ErrLFSObjectNotExist{})
return mockStore
},
expStatusCode: http.StatusNotFound,
expHeader: http.Header{
"Content-Type": []string{"application/vnd.git-lfs+json"},
},
expBody: `{"message":"Object does not exist"}` + "\n",
},
{
name: "storage not found",
mockStore: func() *MockStore {
mockStore := NewMockStore()
mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(&database.LFSObject{Storage: "bad_storage"}, nil)
return mockStore
},
expStatusCode: http.StatusInternalServerError,
expHeader: http.Header{
"Content-Type": []string{"application/vnd.git-lfs+json"},
},
expBody: `{"message":"Internal server error"}` + "\n",
},
{
name: "object exists",
content: "Hello world!",
mockStore: func() *MockStore {
mockStore := NewMockStore()
mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(
&database.LFSObject{
Size: 12,
Storage: s.Storage(),
},
nil,
)
return mockStore
},
expStatusCode: http.StatusOK,
expHeader: http.Header{
"Content-Type": []string{"application/octet-stream"},
"Content-Length": []string{"12"},
},
expBody: "Hello world!",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
basic.store = test.mockStore()
s.buf = bytes.NewBufferString(test.content)
r, err := http.NewRequest(http.MethodGet, "/", nil)
require.NoError(t, err)
rr := httptest.NewRecorder()
m.ServeHTTP(rr, r)
resp := rr.Result()
assert.Equal(t, test.expStatusCode, resp.StatusCode)
assert.Equal(t, test.expHeader, resp.Header)
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
assert.Equal(t, test.expBody, string(body))
})
}
}
func TestBasicHandler_serveUpload(t *testing.T) {
s := &mockStorage{buf: &bytes.Buffer{}}
basic := &basicHandler{
defaultStorage: s.Storage(),
storagers: map[lfsutil.Storage]lfsutil.Storager{
s.Storage(): s,
},
}
m := macaron.New()
m.Use(macaron.Renderer())
m.Use(func(c *macaron.Context) {
c.Map(&database.Repository{Name: "repo"})
c.Map(lfsutil.OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f"))
})
m.Put("/", basic.serveUpload)
tests := []struct {
name string
mockStore func() *MockStore
expStatusCode int
expBody string
}{
{
name: "object already exists",
mockStore: func() *MockStore {
mockStore := NewMockStore()
mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(&database.LFSObject{}, nil)
return mockStore
},
expStatusCode: http.StatusOK,
},
{
name: "new object",
mockStore: func() *MockStore {
mockStore := NewMockStore()
mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(nil, database.ErrLFSObjectNotExist{})
return mockStore
},
expStatusCode: http.StatusOK,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
basic.store = test.mockStore()
r, err := http.NewRequest("PUT", "/", strings.NewReader("Hello world!"))
require.NoError(t, err)
rr := httptest.NewRecorder()
m.ServeHTTP(rr, r)
resp := rr.Result()
assert.Equal(t, test.expStatusCode, resp.StatusCode)
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
assert.Equal(t, test.expBody, string(body))
})
}
}
func TestBasicHandler_serveVerify(t *testing.T) {
basic := &basicHandler{}
m := macaron.New()
m.Use(macaron.Renderer())
m.Use(func(c *macaron.Context) {
c.Map(&database.Repository{Name: "repo"})
})
m.Post("/", basic.serveVerify)
tests := []struct {
name string
body string
mockStore func() *MockStore
expStatusCode int
expBody string
}{
{
name: "invalid oid",
body: `{"oid": "bad_oid"}`,
expStatusCode: http.StatusBadRequest,
expBody: `{"message":"Invalid oid"}` + "\n",
},
{
name: "object does not exist",
body: `{"oid":"ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f"}`,
mockStore: func() *MockStore {
mockStore := NewMockStore()
mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(nil, database.ErrLFSObjectNotExist{})
return mockStore
},
expStatusCode: http.StatusNotFound,
expBody: `{"message":"Object does not exist"}` + "\n",
},
{
name: "object size mismatch",
body: `{"oid":"ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f"}`,
mockStore: func() *MockStore {
mockStore := NewMockStore()
mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(&database.LFSObject{Size: 12}, nil)
return mockStore
},
expStatusCode: http.StatusBadRequest,
expBody: `{"message":"Object size mismatch"}` + "\n",
},
{
name: "object exists",
body: `{"oid":"ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f", "size":12}`,
mockStore: func() *MockStore {
mockStore := NewMockStore()
mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(&database.LFSObject{Size: 12}, nil)
return mockStore
},
expStatusCode: http.StatusOK,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if test.mockStore != nil {
basic.store = test.mockStore()
}
r, err := http.NewRequest("POST", "/", strings.NewReader(test.body))
require.NoError(t, err)
rr := httptest.NewRecorder()
m.ServeHTTP(rr, r)
resp := rr.Result()
assert.Equal(t, test.expStatusCode, resp.StatusCode)
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
assert.Equal(t, test.expBody, string(body))
})
}
}