basic_test.go 7.0 KB

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