1
0

basic_test.go 6.9 KB

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