code.gitea.io/gitea@v1.22.3/tests/integration/lfs_getobject_test.go (about) 1 // Copyright 2019 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package integration 5 6 import ( 7 "archive/zip" 8 "bytes" 9 "io" 10 "net/http" 11 "net/http/httptest" 12 "testing" 13 14 "code.gitea.io/gitea/models/auth" 15 "code.gitea.io/gitea/models/db" 16 git_model "code.gitea.io/gitea/models/git" 17 repo_model "code.gitea.io/gitea/models/repo" 18 "code.gitea.io/gitea/modules/json" 19 "code.gitea.io/gitea/modules/lfs" 20 "code.gitea.io/gitea/modules/setting" 21 "code.gitea.io/gitea/routers/web" 22 "code.gitea.io/gitea/tests" 23 24 gzipp "github.com/klauspost/compress/gzip" 25 "github.com/stretchr/testify/assert" 26 ) 27 28 func storeObjectInRepo(t *testing.T, repositoryID int64, content *[]byte) string { 29 pointer, err := lfs.GeneratePointer(bytes.NewReader(*content)) 30 assert.NoError(t, err) 31 32 _, err = git_model.NewLFSMetaObject(db.DefaultContext, repositoryID, pointer) 33 assert.NoError(t, err) 34 contentStore := lfs.NewContentStore() 35 exist, err := contentStore.Exists(pointer) 36 assert.NoError(t, err) 37 if !exist { 38 err := contentStore.Put(pointer, bytes.NewReader(*content)) 39 assert.NoError(t, err) 40 } 41 return pointer.Oid 42 } 43 44 func storeAndGetLfsToken(t *testing.T, content *[]byte, extraHeader *http.Header, expectedStatus int, ts ...auth.AccessTokenScope) *httptest.ResponseRecorder { 45 repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "repo1") 46 assert.NoError(t, err) 47 oid := storeObjectInRepo(t, repo.ID, content) 48 defer git_model.RemoveLFSMetaObjectByOid(db.DefaultContext, repo.ID, oid) 49 50 token := getUserToken(t, "user2", ts...) 51 52 // Request OID 53 req := NewRequest(t, "GET", "/user2/repo1.git/info/lfs/objects/"+oid+"/test") 54 req.Header.Set("Accept-Encoding", "gzip") 55 req.SetBasicAuth("user2", token) 56 if extraHeader != nil { 57 for key, values := range *extraHeader { 58 for _, value := range values { 59 req.Header.Add(key, value) 60 } 61 } 62 } 63 64 resp := MakeRequest(t, req, expectedStatus) 65 66 return resp 67 } 68 69 func storeAndGetLfs(t *testing.T, content *[]byte, extraHeader *http.Header, expectedStatus int) *httptest.ResponseRecorder { 70 repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "repo1") 71 assert.NoError(t, err) 72 oid := storeObjectInRepo(t, repo.ID, content) 73 defer git_model.RemoveLFSMetaObjectByOid(db.DefaultContext, repo.ID, oid) 74 75 session := loginUser(t, "user2") 76 77 // Request OID 78 req := NewRequest(t, "GET", "/user2/repo1.git/info/lfs/objects/"+oid+"/test") 79 req.Header.Set("Accept-Encoding", "gzip") 80 if extraHeader != nil { 81 for key, values := range *extraHeader { 82 for _, value := range values { 83 req.Header.Add(key, value) 84 } 85 } 86 } 87 88 resp := session.MakeRequest(t, req, expectedStatus) 89 90 return resp 91 } 92 93 func checkResponseTestContentEncoding(t *testing.T, content *[]byte, resp *httptest.ResponseRecorder, expectGzip bool) { 94 contentEncoding := resp.Header().Get("Content-Encoding") 95 if !expectGzip || !setting.EnableGzip { 96 assert.NotContains(t, contentEncoding, "gzip") 97 98 result := resp.Body.Bytes() 99 assert.Equal(t, *content, result) 100 } else { 101 assert.Contains(t, contentEncoding, "gzip") 102 gzippReader, err := gzipp.NewReader(resp.Body) 103 assert.NoError(t, err) 104 result, err := io.ReadAll(gzippReader) 105 assert.NoError(t, err) 106 assert.Equal(t, *content, result) 107 } 108 } 109 110 func TestGetLFSSmall(t *testing.T) { 111 defer tests.PrepareTestEnv(t)() 112 content := []byte("A very small file\n") 113 114 resp := storeAndGetLfs(t, &content, nil, http.StatusOK) 115 checkResponseTestContentEncoding(t, &content, resp, false) 116 } 117 118 func TestGetLFSSmallToken(t *testing.T) { 119 defer tests.PrepareTestEnv(t)() 120 content := []byte("A very small file\n") 121 122 resp := storeAndGetLfsToken(t, &content, nil, http.StatusOK, auth.AccessTokenScopePublicOnly, auth.AccessTokenScopeReadRepository) 123 checkResponseTestContentEncoding(t, &content, resp, false) 124 } 125 126 func TestGetLFSSmallTokenFail(t *testing.T) { 127 defer tests.PrepareTestEnv(t)() 128 content := []byte("A very small file\n") 129 130 storeAndGetLfsToken(t, &content, nil, http.StatusForbidden, auth.AccessTokenScopeReadNotification) 131 } 132 133 func TestGetLFSLarge(t *testing.T) { 134 defer tests.PrepareTestEnv(t)() 135 content := make([]byte, web.GzipMinSize*10) 136 for i := range content { 137 content[i] = byte(i % 256) 138 } 139 140 resp := storeAndGetLfs(t, &content, nil, http.StatusOK) 141 checkResponseTestContentEncoding(t, &content, resp, true) 142 } 143 144 func TestGetLFSGzip(t *testing.T) { 145 defer tests.PrepareTestEnv(t)() 146 b := make([]byte, web.GzipMinSize*10) 147 for i := range b { 148 b[i] = byte(i % 256) 149 } 150 outputBuffer := bytes.NewBuffer([]byte{}) 151 gzippWriter := gzipp.NewWriter(outputBuffer) 152 gzippWriter.Write(b) 153 gzippWriter.Close() 154 content := outputBuffer.Bytes() 155 156 resp := storeAndGetLfs(t, &content, nil, http.StatusOK) 157 checkResponseTestContentEncoding(t, &content, resp, false) 158 } 159 160 func TestGetLFSZip(t *testing.T) { 161 defer tests.PrepareTestEnv(t)() 162 b := make([]byte, web.GzipMinSize*10) 163 for i := range b { 164 b[i] = byte(i % 256) 165 } 166 outputBuffer := bytes.NewBuffer([]byte{}) 167 zipWriter := zip.NewWriter(outputBuffer) 168 fileWriter, err := zipWriter.Create("default") 169 assert.NoError(t, err) 170 fileWriter.Write(b) 171 zipWriter.Close() 172 content := outputBuffer.Bytes() 173 174 resp := storeAndGetLfs(t, &content, nil, http.StatusOK) 175 checkResponseTestContentEncoding(t, &content, resp, false) 176 } 177 178 func TestGetLFSRangeNo(t *testing.T) { 179 defer tests.PrepareTestEnv(t)() 180 content := []byte("123456789\n") 181 182 resp := storeAndGetLfs(t, &content, nil, http.StatusOK) 183 assert.Equal(t, content, resp.Body.Bytes()) 184 } 185 186 func TestGetLFSRange(t *testing.T) { 187 defer tests.PrepareTestEnv(t)() 188 content := []byte("123456789\n") 189 190 tests := []struct { 191 in string 192 out string 193 status int 194 }{ 195 {"bytes=0-0", "1", http.StatusPartialContent}, 196 {"bytes=0-1", "12", http.StatusPartialContent}, 197 {"bytes=1-1", "2", http.StatusPartialContent}, 198 {"bytes=1-3", "234", http.StatusPartialContent}, 199 {"bytes=1-", "23456789\n", http.StatusPartialContent}, 200 // end-range smaller than start-range is ignored 201 {"bytes=1-0", "23456789\n", http.StatusPartialContent}, 202 {"bytes=0-10", "123456789\n", http.StatusPartialContent}, 203 // end-range bigger than length-1 is ignored 204 {"bytes=0-11", "123456789\n", http.StatusPartialContent}, 205 {"bytes=11-", "Requested Range Not Satisfiable", http.StatusRequestedRangeNotSatisfiable}, 206 // incorrect header value cause whole header to be ignored 207 {"bytes=-", "123456789\n", http.StatusOK}, 208 {"foobar", "123456789\n", http.StatusOK}, 209 } 210 211 for _, tt := range tests { 212 t.Run(tt.in, func(t *testing.T) { 213 h := http.Header{ 214 "Range": []string{tt.in}, 215 } 216 resp := storeAndGetLfs(t, &content, &h, tt.status) 217 if tt.status == http.StatusPartialContent || tt.status == http.StatusOK { 218 assert.Equal(t, tt.out, resp.Body.String()) 219 } else { 220 var er lfs.ErrorResponse 221 err := json.Unmarshal(resp.Body.Bytes(), &er) 222 assert.NoError(t, err) 223 assert.Equal(t, tt.out, er.Message) 224 } 225 }) 226 } 227 }