2021-04-09 01:25:57 +03:00
// Copyright 2021 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2021-04-09 01:25:57 +03:00
package lfs
import (
"bytes"
"context"
"io"
"net/http"
"strings"
"testing"
2021-07-24 19:03:58 +03:00
"code.gitea.io/gitea/modules/json"
2024-11-04 07:49:08 +03:00
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
2021-07-24 19:03:58 +03:00
2021-04-09 01:25:57 +03:00
"github.com/stretchr/testify/assert"
)
type RoundTripFunc func ( req * http . Request ) * http . Response
func ( f RoundTripFunc ) RoundTrip ( req * http . Request ) ( * http . Response , error ) {
return f ( req ) , nil
}
2022-01-20 20:46:10 +03:00
type DummyTransferAdapter struct { }
2021-04-09 01:25:57 +03:00
func ( a * DummyTransferAdapter ) Name ( ) string {
return "dummy"
}
2021-06-14 20:20:43 +03:00
func ( a * DummyTransferAdapter ) Download ( ctx context . Context , l * Link ) ( io . ReadCloser , error ) {
2021-09-22 08:38:34 +03:00
return io . NopCloser ( bytes . NewBufferString ( "dummy" ) ) , nil
2021-04-09 01:25:57 +03:00
}
2021-06-14 20:20:43 +03:00
func ( a * DummyTransferAdapter ) Upload ( ctx context . Context , l * Link , p Pointer , r io . Reader ) error {
return nil
}
func ( a * DummyTransferAdapter ) Verify ( ctx context . Context , l * Link , p Pointer ) error {
return nil
}
func lfsTestRoundtripHandler ( req * http . Request ) * http . Response {
var batchResponse * BatchResponse
url := req . URL . String ( )
2021-04-09 01:25:57 +03:00
2021-06-14 20:20:43 +03:00
if strings . Contains ( url , "status-not-ok" ) {
return & http . Response { StatusCode : http . StatusBadRequest }
} else if strings . Contains ( url , "invalid-json-response" ) {
2021-09-22 08:38:34 +03:00
return & http . Response { StatusCode : http . StatusOK , Body : io . NopCloser ( bytes . NewBufferString ( "invalid json" ) ) }
2021-06-14 20:20:43 +03:00
} else if strings . Contains ( url , "valid-batch-request-download" ) {
batchResponse = & BatchResponse {
Transfer : "dummy" ,
Objects : [ ] * ObjectResponse {
{
Actions : map [ string ] * Link {
"download" : { } ,
} ,
} ,
} ,
}
2024-06-28 11:42:57 +03:00
} else if strings . Contains ( url , "legacy-batch-request-download" ) {
batchResponse = & BatchResponse {
Transfer : "dummy" ,
Objects : [ ] * ObjectResponse {
{
Links : map [ string ] * Link {
"download" : { } ,
} ,
} ,
} ,
}
2021-06-14 20:20:43 +03:00
} else if strings . Contains ( url , "valid-batch-request-upload" ) {
batchResponse = & BatchResponse {
Transfer : "dummy" ,
Objects : [ ] * ObjectResponse {
{
Actions : map [ string ] * Link {
"upload" : { } ,
} ,
} ,
} ,
2021-04-09 01:25:57 +03:00
}
2021-06-14 20:20:43 +03:00
} else if strings . Contains ( url , "response-no-objects" ) {
batchResponse = & BatchResponse { Transfer : "dummy" }
} else if strings . Contains ( url , "unknown-transfer-adapter" ) {
batchResponse = & BatchResponse { Transfer : "unknown_adapter" }
} else if strings . Contains ( url , "error-in-response-objects" ) {
batchResponse = & BatchResponse {
Transfer : "dummy" ,
Objects : [ ] * ObjectResponse {
{
Error : & ObjectError {
2022-03-23 07:54:07 +03:00
Code : http . StatusNotFound ,
2021-06-14 20:20:43 +03:00
Message : "Object not found" ,
} ,
} ,
} ,
2021-04-09 01:25:57 +03:00
}
2021-06-14 20:20:43 +03:00
} else if strings . Contains ( url , "empty-actions-map" ) {
batchResponse = & BatchResponse {
Transfer : "dummy" ,
Objects : [ ] * ObjectResponse {
{
Actions : map [ string ] * Link { } ,
} ,
} ,
}
} else if strings . Contains ( url , "download-actions-map" ) {
batchResponse = & BatchResponse {
Transfer : "dummy" ,
Objects : [ ] * ObjectResponse {
{
Actions : map [ string ] * Link {
"download" : { } ,
} ,
} ,
} ,
}
} else if strings . Contains ( url , "upload-actions-map" ) {
batchResponse = & BatchResponse {
Transfer : "dummy" ,
Objects : [ ] * ObjectResponse {
{
Actions : map [ string ] * Link {
"upload" : { } ,
} ,
} ,
} ,
}
} else if strings . Contains ( url , "verify-actions-map" ) {
batchResponse = & BatchResponse {
Transfer : "dummy" ,
Objects : [ ] * ObjectResponse {
{
Actions : map [ string ] * Link {
"verify" : { } ,
} ,
} ,
} ,
}
} else if strings . Contains ( url , "unknown-actions-map" ) {
batchResponse = & BatchResponse {
Transfer : "dummy" ,
Objects : [ ] * ObjectResponse {
{
Actions : map [ string ] * Link {
"unknown" : { } ,
} ,
} ,
} ,
}
} else {
return nil
}
2021-04-09 01:25:57 +03:00
2021-06-14 20:20:43 +03:00
payload := new ( bytes . Buffer )
2021-07-24 19:03:58 +03:00
json . NewEncoder ( payload ) . Encode ( batchResponse )
2021-04-09 01:25:57 +03:00
2021-09-22 08:38:34 +03:00
return & http . Response { StatusCode : http . StatusOK , Body : io . NopCloser ( payload ) }
2021-06-14 20:20:43 +03:00
}
2021-04-09 01:25:57 +03:00
2021-06-14 20:20:43 +03:00
func TestHTTPClientDownload ( t * testing . T ) {
p := Pointer { Oid : "fb8f7d8435968c4f82a726a92395be4d16f2f63116caf36c8ad35c60831ab041" , Size : 6 }
2021-04-09 01:25:57 +03:00
2021-06-14 20:20:43 +03:00
hc := & http . Client { Transport : RoundTripFunc ( func ( req * http . Request ) * http . Response {
assert . Equal ( t , "POST" , req . Method )
assert . Equal ( t , MediaType , req . Header . Get ( "Content-type" ) )
2024-06-12 01:22:28 +03:00
assert . Equal ( t , AcceptHeader , req . Header . Get ( "Accept" ) )
2021-04-09 01:25:57 +03:00
2021-06-14 20:20:43 +03:00
var batchRequest BatchRequest
2021-07-24 19:03:58 +03:00
err := json . NewDecoder ( req . Body ) . Decode ( & batchRequest )
2021-06-14 20:20:43 +03:00
assert . NoError ( t , err )
2021-04-09 01:25:57 +03:00
2021-06-14 20:20:43 +03:00
assert . Equal ( t , "download" , batchRequest . Operation )
2023-04-23 00:56:27 +03:00
assert . Len ( t , batchRequest . Objects , 1 )
2021-06-14 20:20:43 +03:00
assert . Equal ( t , p . Oid , batchRequest . Objects [ 0 ] . Oid )
assert . Equal ( t , p . Size , batchRequest . Objects [ 0 ] . Size )
2021-04-09 01:25:57 +03:00
2021-06-14 20:20:43 +03:00
return lfsTestRoundtripHandler ( req )
} ) }
dummy := & DummyTransferAdapter { }
2021-04-09 01:25:57 +03:00
2022-01-20 20:46:10 +03:00
cases := [ ] struct {
2021-06-14 20:20:43 +03:00
endpoint string
2024-11-04 07:49:08 +03:00
expectedError string
2021-06-14 20:20:43 +03:00
} {
{
endpoint : "https://status-not-ok.io" ,
2024-11-04 07:49:08 +03:00
expectedError : io . ErrUnexpectedEOF . Error ( ) ,
2021-06-14 20:20:43 +03:00
} ,
{
endpoint : "https://invalid-json-response.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "invalid json" ,
2021-06-14 20:20:43 +03:00
} ,
{
endpoint : "https://valid-batch-request-download.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "" ,
2021-06-14 20:20:43 +03:00
} ,
{
endpoint : "https://response-no-objects.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "" ,
2021-06-14 20:20:43 +03:00
} ,
{
endpoint : "https://unknown-transfer-adapter.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "TransferAdapter not found: " ,
2021-06-14 20:20:43 +03:00
} ,
{
endpoint : "https://error-in-response-objects.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "Object not found" ,
2021-06-14 20:20:43 +03:00
} ,
{
endpoint : "https://empty-actions-map.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "missing action 'download'" ,
2021-06-14 20:20:43 +03:00
} ,
{
endpoint : "https://download-actions-map.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "" ,
2021-06-14 20:20:43 +03:00
} ,
{
endpoint : "https://upload-actions-map.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "missing action 'download'" ,
2021-06-14 20:20:43 +03:00
} ,
{
endpoint : "https://verify-actions-map.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "missing action 'download'" ,
2021-06-14 20:20:43 +03:00
} ,
{
endpoint : "https://unknown-actions-map.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "missing action 'download'" ,
2021-06-14 20:20:43 +03:00
} ,
2024-06-28 11:42:57 +03:00
{
endpoint : "https://legacy-batch-request-download.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "" ,
2024-06-28 11:42:57 +03:00
} ,
2021-06-14 20:20:43 +03:00
}
2021-04-09 01:25:57 +03:00
2024-11-05 16:10:57 +03:00
defer test . MockVariableValue ( & setting . LFSClient . BatchOperationConcurrency , 8 ) ( )
2024-11-04 07:49:08 +03:00
for _ , c := range cases {
t . Run ( c . endpoint , func ( t * testing . T ) {
client := & HTTPClient {
client : hc ,
endpoint : c . endpoint ,
transfers : map [ string ] TransferAdapter {
"dummy" : dummy ,
} ,
}
2021-04-09 01:25:57 +03:00
2024-11-04 07:49:08 +03:00
err := client . Download ( context . Background ( ) , [ ] Pointer { p } , func ( p Pointer , content io . ReadCloser , objectError error ) error {
if objectError != nil {
return objectError
}
b , err := io . ReadAll ( content )
assert . NoError ( t , err )
assert . Equal ( t , [ ] byte ( "dummy" ) , b )
return nil
} )
if c . expectedError != "" {
assert . ErrorContains ( t , err , c . expectedError )
} else {
assert . NoError ( t , err )
2021-06-14 20:20:43 +03:00
}
} )
2021-04-09 01:25:57 +03:00
}
2021-06-14 20:20:43 +03:00
}
func TestHTTPClientUpload ( t * testing . T ) {
p := Pointer { Oid : "fb8f7d8435968c4f82a726a92395be4d16f2f63116caf36c8ad35c60831ab041" , Size : 6 }
hc := & http . Client { Transport : RoundTripFunc ( func ( req * http . Request ) * http . Response {
assert . Equal ( t , "POST" , req . Method )
assert . Equal ( t , MediaType , req . Header . Get ( "Content-type" ) )
2024-06-12 01:22:28 +03:00
assert . Equal ( t , AcceptHeader , req . Header . Get ( "Accept" ) )
2021-06-14 20:20:43 +03:00
var batchRequest BatchRequest
2021-07-24 19:03:58 +03:00
err := json . NewDecoder ( req . Body ) . Decode ( & batchRequest )
2021-06-14 20:20:43 +03:00
assert . NoError ( t , err )
2021-04-09 01:25:57 +03:00
2021-06-14 20:20:43 +03:00
assert . Equal ( t , "upload" , batchRequest . Operation )
2023-04-23 00:56:27 +03:00
assert . Len ( t , batchRequest . Objects , 1 )
2021-06-14 20:20:43 +03:00
assert . Equal ( t , p . Oid , batchRequest . Objects [ 0 ] . Oid )
assert . Equal ( t , p . Size , batchRequest . Objects [ 0 ] . Size )
return lfsTestRoundtripHandler ( req )
} ) }
2021-04-09 01:25:57 +03:00
dummy := & DummyTransferAdapter { }
2022-01-20 20:46:10 +03:00
cases := [ ] struct {
2021-04-09 01:25:57 +03:00
endpoint string
2024-11-04 07:49:08 +03:00
expectedError string
2021-04-09 01:25:57 +03:00
} {
{
endpoint : "https://status-not-ok.io" ,
2024-11-04 07:49:08 +03:00
expectedError : io . ErrUnexpectedEOF . Error ( ) ,
2021-04-09 01:25:57 +03:00
} ,
{
endpoint : "https://invalid-json-response.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "invalid json" ,
2021-04-09 01:25:57 +03:00
} ,
{
2021-06-14 20:20:43 +03:00
endpoint : "https://valid-batch-request-upload.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "" ,
2021-04-09 01:25:57 +03:00
} ,
{
2021-06-14 20:20:43 +03:00
endpoint : "https://response-no-objects.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "" ,
2021-04-09 01:25:57 +03:00
} ,
{
endpoint : "https://unknown-transfer-adapter.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "TransferAdapter not found: " ,
2021-06-14 20:20:43 +03:00
} ,
{
endpoint : "https://error-in-response-objects.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "Object not found" ,
2021-06-14 20:20:43 +03:00
} ,
{
endpoint : "https://empty-actions-map.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "" ,
2021-06-14 20:20:43 +03:00
} ,
{
endpoint : "https://download-actions-map.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "missing action 'upload'" ,
2021-06-14 20:20:43 +03:00
} ,
{
endpoint : "https://upload-actions-map.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "" ,
2021-06-14 20:20:43 +03:00
} ,
{
endpoint : "https://verify-actions-map.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "missing action 'upload'" ,
2021-06-14 20:20:43 +03:00
} ,
{
endpoint : "https://unknown-actions-map.io" ,
2024-11-04 07:49:08 +03:00
expectedError : "missing action 'upload'" ,
2021-04-09 01:25:57 +03:00
} ,
}
2024-11-05 16:10:57 +03:00
defer test . MockVariableValue ( & setting . LFSClient . BatchOperationConcurrency , 8 ) ( )
2024-11-04 07:49:08 +03:00
for _ , c := range cases {
t . Run ( c . endpoint , func ( t * testing . T ) {
client := & HTTPClient {
client : hc ,
endpoint : c . endpoint ,
transfers : map [ string ] TransferAdapter {
"dummy" : dummy ,
} ,
}
2021-04-09 01:25:57 +03:00
2024-11-04 07:49:08 +03:00
err := client . Upload ( context . Background ( ) , [ ] Pointer { p } , func ( p Pointer , objectError error ) ( io . ReadCloser , error ) {
return io . NopCloser ( new ( bytes . Buffer ) ) , objectError
} )
if c . expectedError != "" {
assert . ErrorContains ( t , err , c . expectedError )
} else {
assert . NoError ( t , err )
}
2021-06-14 20:20:43 +03:00
} )
2021-04-09 01:25:57 +03:00
}
}