basic_test.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. // Copyright 2020 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package lfs
  5. import (
  6. "bytes"
  7. "io"
  8. "net/http"
  9. "net/http/httptest"
  10. "strings"
  11. "testing"
  12. "github.com/stretchr/testify/assert"
  13. "github.com/stretchr/testify/require"
  14. "gopkg.in/macaron.v1"
  15. "gogs.io/gogs/internal/database"
  16. "gogs.io/gogs/internal/lfsutil"
  17. )
  18. var _ lfsutil.Storager = (*mockStorage)(nil)
  19. // mockStorage is an in-memory storage for LFS objects.
  20. type mockStorage struct {
  21. buf *bytes.Buffer
  22. }
  23. func (*mockStorage) Storage() lfsutil.Storage {
  24. return "memory"
  25. }
  26. func (s *mockStorage) Upload(_ lfsutil.OID, rc io.ReadCloser) (int64, error) {
  27. defer func() { _ = rc.Close() }()
  28. return io.Copy(s.buf, rc)
  29. }
  30. func (s *mockStorage) Download(_ lfsutil.OID, w io.Writer) error {
  31. _, err := io.Copy(w, s.buf)
  32. return err
  33. }
  34. func TestBasicHandler_serveDownload(t *testing.T) {
  35. s := &mockStorage{}
  36. basic := &basicHandler{
  37. defaultStorage: s.Storage(),
  38. storagers: map[lfsutil.Storage]lfsutil.Storager{
  39. s.Storage(): s,
  40. },
  41. }
  42. m := macaron.New()
  43. m.Use(macaron.Renderer())
  44. m.Use(func(c *macaron.Context) {
  45. c.Map(&database.Repository{Name: "repo"})
  46. c.Map(lfsutil.OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f"))
  47. })
  48. m.Get("/", basic.serveDownload)
  49. tests := []struct {
  50. name string
  51. content string
  52. mockStore func() *MockStore
  53. expStatusCode int
  54. expHeader http.Header
  55. expBody string
  56. }{
  57. {
  58. name: "object does not exist",
  59. mockStore: func() *MockStore {
  60. mockStore := NewMockStore()
  61. mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(nil, database.ErrLFSObjectNotExist{})
  62. return mockStore
  63. },
  64. expStatusCode: http.StatusNotFound,
  65. expHeader: http.Header{
  66. "Content-Type": []string{"application/vnd.git-lfs+json"},
  67. },
  68. expBody: `{"message":"Object does not exist"}` + "\n",
  69. },
  70. {
  71. name: "storage not found",
  72. mockStore: func() *MockStore {
  73. mockStore := NewMockStore()
  74. mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(&database.LFSObject{Storage: "bad_storage"}, nil)
  75. return mockStore
  76. },
  77. expStatusCode: http.StatusInternalServerError,
  78. expHeader: http.Header{
  79. "Content-Type": []string{"application/vnd.git-lfs+json"},
  80. },
  81. expBody: `{"message":"Internal server error"}` + "\n",
  82. },
  83. {
  84. name: "object exists",
  85. content: "Hello world!",
  86. mockStore: func() *MockStore {
  87. mockStore := NewMockStore()
  88. mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(
  89. &database.LFSObject{
  90. Size: 12,
  91. Storage: s.Storage(),
  92. },
  93. nil,
  94. )
  95. return mockStore
  96. },
  97. expStatusCode: http.StatusOK,
  98. expHeader: http.Header{
  99. "Content-Type": []string{"application/octet-stream"},
  100. "Content-Length": []string{"12"},
  101. },
  102. expBody: "Hello world!",
  103. },
  104. }
  105. for _, test := range tests {
  106. t.Run(test.name, func(t *testing.T) {
  107. basic.store = test.mockStore()
  108. s.buf = bytes.NewBufferString(test.content)
  109. r, err := http.NewRequest(http.MethodGet, "/", nil)
  110. require.NoError(t, err)
  111. rr := httptest.NewRecorder()
  112. m.ServeHTTP(rr, r)
  113. resp := rr.Result()
  114. assert.Equal(t, test.expStatusCode, resp.StatusCode)
  115. assert.Equal(t, test.expHeader, resp.Header)
  116. body, err := io.ReadAll(resp.Body)
  117. require.NoError(t, err)
  118. assert.Equal(t, test.expBody, string(body))
  119. })
  120. }
  121. }
  122. func TestBasicHandler_serveUpload(t *testing.T) {
  123. s := &mockStorage{buf: &bytes.Buffer{}}
  124. basic := &basicHandler{
  125. defaultStorage: s.Storage(),
  126. storagers: map[lfsutil.Storage]lfsutil.Storager{
  127. s.Storage(): s,
  128. },
  129. }
  130. m := macaron.New()
  131. m.Use(macaron.Renderer())
  132. m.Use(func(c *macaron.Context) {
  133. c.Map(&database.Repository{Name: "repo"})
  134. c.Map(lfsutil.OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f"))
  135. })
  136. m.Put("/", basic.serveUpload)
  137. tests := []struct {
  138. name string
  139. mockStore func() *MockStore
  140. expStatusCode int
  141. expBody string
  142. }{
  143. {
  144. name: "object already exists",
  145. mockStore: func() *MockStore {
  146. mockStore := NewMockStore()
  147. mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(&database.LFSObject{}, nil)
  148. return mockStore
  149. },
  150. expStatusCode: http.StatusOK,
  151. },
  152. {
  153. name: "new object",
  154. mockStore: func() *MockStore {
  155. mockStore := NewMockStore()
  156. mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(nil, database.ErrLFSObjectNotExist{})
  157. return mockStore
  158. },
  159. expStatusCode: http.StatusOK,
  160. },
  161. }
  162. for _, test := range tests {
  163. t.Run(test.name, func(t *testing.T) {
  164. basic.store = test.mockStore()
  165. r, err := http.NewRequest("PUT", "/", strings.NewReader("Hello world!"))
  166. require.NoError(t, err)
  167. rr := httptest.NewRecorder()
  168. m.ServeHTTP(rr, r)
  169. resp := rr.Result()
  170. assert.Equal(t, test.expStatusCode, resp.StatusCode)
  171. body, err := io.ReadAll(resp.Body)
  172. require.NoError(t, err)
  173. assert.Equal(t, test.expBody, string(body))
  174. })
  175. }
  176. }
  177. func TestBasicHandler_serveVerify(t *testing.T) {
  178. basic := &basicHandler{}
  179. m := macaron.New()
  180. m.Use(macaron.Renderer())
  181. m.Use(func(c *macaron.Context) {
  182. c.Map(&database.Repository{Name: "repo"})
  183. })
  184. m.Post("/", basic.serveVerify)
  185. tests := []struct {
  186. name string
  187. body string
  188. mockStore func() *MockStore
  189. expStatusCode int
  190. expBody string
  191. }{
  192. {
  193. name: "invalid oid",
  194. body: `{"oid": "bad_oid"}`,
  195. expStatusCode: http.StatusBadRequest,
  196. expBody: `{"message":"Invalid oid"}` + "\n",
  197. },
  198. {
  199. name: "object does not exist",
  200. body: `{"oid":"ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f"}`,
  201. mockStore: func() *MockStore {
  202. mockStore := NewMockStore()
  203. mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(nil, database.ErrLFSObjectNotExist{})
  204. return mockStore
  205. },
  206. expStatusCode: http.StatusNotFound,
  207. expBody: `{"message":"Object does not exist"}` + "\n",
  208. },
  209. {
  210. name: "object size mismatch",
  211. body: `{"oid":"ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f"}`,
  212. mockStore: func() *MockStore {
  213. mockStore := NewMockStore()
  214. mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(&database.LFSObject{Size: 12}, nil)
  215. return mockStore
  216. },
  217. expStatusCode: http.StatusBadRequest,
  218. expBody: `{"message":"Object size mismatch"}` + "\n",
  219. },
  220. {
  221. name: "object exists",
  222. body: `{"oid":"ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f", "size":12}`,
  223. mockStore: func() *MockStore {
  224. mockStore := NewMockStore()
  225. mockStore.GetLFSObjectByOIDFunc.SetDefaultReturn(&database.LFSObject{Size: 12}, nil)
  226. return mockStore
  227. },
  228. expStatusCode: http.StatusOK,
  229. },
  230. }
  231. for _, test := range tests {
  232. t.Run(test.name, func(t *testing.T) {
  233. if test.mockStore != nil {
  234. basic.store = test.mockStore()
  235. }
  236. r, err := http.NewRequest("POST", "/", strings.NewReader(test.body))
  237. require.NoError(t, err)
  238. rr := httptest.NewRecorder()
  239. m.ServeHTTP(rr, r)
  240. resp := rr.Result()
  241. assert.Equal(t, test.expStatusCode, resp.StatusCode)
  242. body, err := io.ReadAll(resp.Body)
  243. require.NoError(t, err)
  244. assert.Equal(t, test.expBody, string(body))
  245. })
  246. }
  247. }