2023-05-03 18:02:11 +02:00
//go:build unit
// +build unit
2019-12-09 18:35:31 +02:00
package http
import (
2020-03-23 16:02:22 +02:00
"bytes"
2021-08-19 11:29:33 +02:00
"crypto/tls"
"crypto/x509"
2021-01-22 10:56:56 +02:00
"encoding/base64"
2020-07-14 10:58:57 +02:00
"encoding/xml"
2021-02-08 15:26:15 +02:00
"errors"
2019-12-09 18:35:31 +02:00
"fmt"
"io"
"io/ioutil"
2020-01-14 11:29:50 +02:00
"mime/multipart"
2019-12-09 18:35:31 +02:00
"net/http"
"net/http/httptest"
[TMS] Reimplement tmsUpload step in Go (#3399)
* Initially generated tmsUpload<...> files
* First provisioning of parameters supported by tmsUpload step
* Refer to Go step from tmsUpload.groovy
* Initial client implementation
* Reverting line delimiters in tmsUpoad.groovy back to Unix ones
* Temporarily remove when-condition for Release stage
* Define useGoStep parameter in tmsUpload.groovy
* Unstash buildResult if useGoStep is true
* No unstashing and empty credentials, when using go step
* Register TmsUploadCommand in piper.go
* Cleanup groovy-related changes - they will be temporarily implemented in a different repo
* Make getting OAuth token success
* Look through the code and cleanup it a bit
* Read service key from Jenkins credentials store
* Provide initial set of unit tests for methods in /pkg/tms/tms.go file
* Minor improvements on logging response on http call error
* Check, if positive HTTP status code is as expected
* Cleanup tms.yaml file, provide additional unit test for tms.go
* Provide unit test for the case, when request body contains spaces
* Specify nodeExtDescriptorMapping parameter as of type map in tms.yaml
* Implement client method for getting nodes
* Write tests for GetNodes method
* Add GetMtaExtDescriptor client method and cover it with unit tests
* Provide first implementation for Update- and UploadMtaExtDescriptor
client methods
* Provide first implementation for Update- and UploadMtaExtDescriptor
client methods
* Provide UploadFile and UploadFileToNode client methods
* Provide tests for Update- and UploadMtaExtDescriptor client methods
* Write tests for FileUpload and FileUploadToNode client methods
* Minor corrections
* Remove some TODO comments
* Rename some of response structures
* Revert change for line delimiters in cmd/piper.go
* Add uploadType string parameter to UploadFile and UploadRequest methods
of uploader mock to reflect the changed Uploader implementation
* Start to implement execution logic in tmsUpload.go
* Changes in tms.yaml file
- remove resources from inputs in tms.yaml and implement mtaPath
parameter settings in the yaml file the same way, as it is done in
cloudFoundryDeploy.yaml
- rename tms.yaml to tmsUpload.yaml, since some generation policy
changed meanwhile
* Rename tms.yaml to tmsUpload.yaml and do go generate
* Use provided proxy on communication with UAA and TMS
* Set proxy even before getting OAuth token
* Further implementation of tmsUpload.go
* Continuation on implementing the tmsUpload.go executor
* Get mtarFilePath and git commitId from commonPipelineEnvironment, if
they are missing in configuration file + minor changes
* Implement a happy path test for tmsUpload logic
* Cover with unit tests main happy and error paths of tmsUpload.go logic
* Extend set of unit tests for tmsUpload.go
* Eliminate some TODOs, extend unit tests for tmsUpload.go
* Delete some TODOs
* Remove a couple of more TODOs from tms_test.go file
* Provide additional unit test for error due unexpected positive http
status code on upload
* Revert back line delimiters in cmd/piper.go
* Comment out file uploading calls in tmsUpload.go
* Run go generate to update generated files
* Convert line delimiters in tmsUpload.yaml to Unix ones, as well as
provide new line character in the end of the file, plus minor fix for
logging in tmsUpload.go file (pipeline complained)
* Correct description of a parameter in tmsUpload.yaml, extend unit tests
to check for trimming a slash in the end of TMS url for client methods
that do upload
* [minor] Add a comment in the test code
* Add stashContent parameter to do unstashing in tmsUpload.groovy, remove
some of the clarified TODOs
* Uncomment uploading file calls in tmsUpload.go, declare buildResult
stash in tmsUpload.yaml
* Remove clarified TODOs from the tmsUpload.go file
* Run go fmt for jenkins-library/pkg/tms
* Do not get explicitly values from common pipeline environment - all
configurations are provided in yaml file
* Remove unused struct from tmsUpload_test.go
* Run go fmt jenkins-library\pkg\tms
* Revise descriptions of parameters provided in tmsUpload.yaml file
* Specify STAGES scope for tmsUpload parameters
* Provide STAGES scope for the tmsUpload parameters, provide default value
for stashContent parameter
* Remove trailing space from tmsUpload.yaml
* Provide unit tests for proxy-related changes in http.go file
* Improve proxy implementation in tmsUpload.go file
* Make tmsServiceKey again a mandatory parameter
* Run go generate command to make the generated files correspond the yaml
state
* Change line delimiters back to Unix ones (were switched while resolving
the conflicts)
* Remove trailing spaces from tmsUpload.yaml
* Minor change in a comment to trigger pipelines with commit
* Improve checks for zero-structs and for empty maps, as well as use
different package to read files in the tests
* Revert line endings in http.go
* Revert comments formatting changes in files that do not belong to the tmsUpload step
2022-08-30 10:16:09 +02:00
"net/url"
2020-01-14 11:29:50 +02:00
"os"
2022-03-07 18:15:36 +02:00
"path/filepath"
2019-12-09 18:35:31 +02:00
"testing"
"time"
2021-02-04 15:58:35 +02:00
"github.com/jarcoal/httpmock"
2021-01-22 10:56:56 +02:00
"github.com/sirupsen/logrus"
2019-12-09 18:35:31 +02:00
"github.com/stretchr/testify/assert"
2020-07-14 10:58:57 +02:00
"github.com/stretchr/testify/mock"
2021-02-04 15:58:35 +02:00
"github.com/stretchr/testify/require"
"github.com/SAP/jenkins-library/pkg/log"
2019-12-09 18:35:31 +02:00
)
2021-02-08 15:26:15 +02:00
func TestSend ( t * testing . T ) {
testURL := "https://example.org"
request , err := http . NewRequest ( http . MethodGet , testURL , nil )
require . NoError ( t , err )
t . Run ( "success" , func ( t * testing . T ) {
// given
httpmock . Activate ( )
defer httpmock . DeactivateAndReset ( )
httpmock . RegisterResponder ( http . MethodGet , testURL , httpmock . NewStringResponder ( 200 , ` OK ` ) )
client := Client { }
2021-06-15 11:13:24 +02:00
client . SetOptions ( ClientOptions { MaxRetries : - 1 , UseDefaultTransport : true } )
2021-02-08 15:26:15 +02:00
// when
response , err := client . Send ( request )
// then
assert . NoError ( t , err )
assert . NotNil ( t , response )
} )
t . Run ( "failure" , func ( t * testing . T ) {
// given
httpmock . Activate ( )
defer httpmock . DeactivateAndReset ( )
httpmock . RegisterResponder ( http . MethodGet , testURL , httpmock . NewErrorResponder ( errors . New ( "failure" ) ) )
client := Client { }
2021-06-15 11:13:24 +02:00
client . SetOptions ( ClientOptions { MaxRetries : - 1 , UseDefaultTransport : true } )
2021-02-08 15:26:15 +02:00
// when
response , err := client . Send ( request )
// then
assert . Error ( t , err )
assert . Nil ( t , response )
} )
[TMS] Reimplement tmsUpload step in Go (#3399)
* Initially generated tmsUpload<...> files
* First provisioning of parameters supported by tmsUpload step
* Refer to Go step from tmsUpload.groovy
* Initial client implementation
* Reverting line delimiters in tmsUpoad.groovy back to Unix ones
* Temporarily remove when-condition for Release stage
* Define useGoStep parameter in tmsUpload.groovy
* Unstash buildResult if useGoStep is true
* No unstashing and empty credentials, when using go step
* Register TmsUploadCommand in piper.go
* Cleanup groovy-related changes - they will be temporarily implemented in a different repo
* Make getting OAuth token success
* Look through the code and cleanup it a bit
* Read service key from Jenkins credentials store
* Provide initial set of unit tests for methods in /pkg/tms/tms.go file
* Minor improvements on logging response on http call error
* Check, if positive HTTP status code is as expected
* Cleanup tms.yaml file, provide additional unit test for tms.go
* Provide unit test for the case, when request body contains spaces
* Specify nodeExtDescriptorMapping parameter as of type map in tms.yaml
* Implement client method for getting nodes
* Write tests for GetNodes method
* Add GetMtaExtDescriptor client method and cover it with unit tests
* Provide first implementation for Update- and UploadMtaExtDescriptor
client methods
* Provide first implementation for Update- and UploadMtaExtDescriptor
client methods
* Provide UploadFile and UploadFileToNode client methods
* Provide tests for Update- and UploadMtaExtDescriptor client methods
* Write tests for FileUpload and FileUploadToNode client methods
* Minor corrections
* Remove some TODO comments
* Rename some of response structures
* Revert change for line delimiters in cmd/piper.go
* Add uploadType string parameter to UploadFile and UploadRequest methods
of uploader mock to reflect the changed Uploader implementation
* Start to implement execution logic in tmsUpload.go
* Changes in tms.yaml file
- remove resources from inputs in tms.yaml and implement mtaPath
parameter settings in the yaml file the same way, as it is done in
cloudFoundryDeploy.yaml
- rename tms.yaml to tmsUpload.yaml, since some generation policy
changed meanwhile
* Rename tms.yaml to tmsUpload.yaml and do go generate
* Use provided proxy on communication with UAA and TMS
* Set proxy even before getting OAuth token
* Further implementation of tmsUpload.go
* Continuation on implementing the tmsUpload.go executor
* Get mtarFilePath and git commitId from commonPipelineEnvironment, if
they are missing in configuration file + minor changes
* Implement a happy path test for tmsUpload logic
* Cover with unit tests main happy and error paths of tmsUpload.go logic
* Extend set of unit tests for tmsUpload.go
* Eliminate some TODOs, extend unit tests for tmsUpload.go
* Delete some TODOs
* Remove a couple of more TODOs from tms_test.go file
* Provide additional unit test for error due unexpected positive http
status code on upload
* Revert back line delimiters in cmd/piper.go
* Comment out file uploading calls in tmsUpload.go
* Run go generate to update generated files
* Convert line delimiters in tmsUpload.yaml to Unix ones, as well as
provide new line character in the end of the file, plus minor fix for
logging in tmsUpload.go file (pipeline complained)
* Correct description of a parameter in tmsUpload.yaml, extend unit tests
to check for trimming a slash in the end of TMS url for client methods
that do upload
* [minor] Add a comment in the test code
* Add stashContent parameter to do unstashing in tmsUpload.groovy, remove
some of the clarified TODOs
* Uncomment uploading file calls in tmsUpload.go, declare buildResult
stash in tmsUpload.yaml
* Remove clarified TODOs from the tmsUpload.go file
* Run go fmt for jenkins-library/pkg/tms
* Do not get explicitly values from common pipeline environment - all
configurations are provided in yaml file
* Remove unused struct from tmsUpload_test.go
* Run go fmt jenkins-library\pkg\tms
* Revise descriptions of parameters provided in tmsUpload.yaml file
* Specify STAGES scope for tmsUpload parameters
* Provide STAGES scope for the tmsUpload parameters, provide default value
for stashContent parameter
* Remove trailing space from tmsUpload.yaml
* Provide unit tests for proxy-related changes in http.go file
* Improve proxy implementation in tmsUpload.go file
* Make tmsServiceKey again a mandatory parameter
* Run go generate command to make the generated files correspond the yaml
state
* Change line delimiters back to Unix ones (were switched while resolving
the conflicts)
* Remove trailing spaces from tmsUpload.yaml
* Minor change in a comment to trigger pipelines with commit
* Improve checks for zero-structs and for empty maps, as well as use
different package to read files in the tests
* Revert line endings in http.go
* Revert comments formatting changes in files that do not belong to the tmsUpload step
2022-08-30 10:16:09 +02:00
t . Run ( "failure when calling via proxy" , func ( t * testing . T ) {
// given
httpmock . Activate ( )
defer httpmock . DeactivateAndReset ( )
httpmock . RegisterResponder ( http . MethodGet , testURL , httpmock . NewStringResponder ( 200 , ` OK ` ) )
client := Client { }
transportProxy , _ := url . Parse ( "https://proxy.dummy.sap.com" )
client . SetOptions ( ClientOptions { MaxRetries : - 1 , TransportProxy : transportProxy } )
// when
response , err := client . Send ( request )
// then
assert . Error ( t , err )
assert . Contains ( t , err . Error ( ) , "lookup proxy.dummy.sap.com: no such host" )
assert . Nil ( t , response )
} )
2021-02-08 15:26:15 +02:00
}
2021-02-04 15:58:35 +02:00
func TestDefaultTransport ( t * testing . T ) {
const testURL string = "https://localhost/api"
t . Run ( "with default transport" , func ( t * testing . T ) {
httpmock . Activate ( )
defer httpmock . DeactivateAndReset ( )
httpmock . RegisterResponder ( http . MethodGet , testURL , httpmock . NewStringResponder ( 200 , ` OK ` ) )
client := Client { }
2021-06-15 11:13:24 +02:00
client . SetOptions ( ClientOptions { MaxRetries : - 1 , UseDefaultTransport : true } )
2021-02-04 15:58:35 +02:00
// test
response , err := client . SendRequest ( "GET" , testURL , nil , nil , nil )
// assert
assert . NoError ( t , err )
// assert.NotEmpty(t, count)
assert . Equal ( t , 1 , httpmock . GetTotalCallCount ( ) , "unexpected number of requests" )
content , err := ioutil . ReadAll ( response . Body )
defer response . Body . Close ( )
require . NoError ( t , err , "unexpected error while reading response body" )
assert . Equal ( t , "OK" , string ( content ) , "unexpected response content" )
} )
t . Run ( "with custom transport" , func ( t * testing . T ) {
httpmock . Activate ( )
defer httpmock . DeactivateAndReset ( )
httpmock . RegisterResponder ( http . MethodGet , testURL , httpmock . NewStringResponder ( 200 , ` OK ` ) )
client := Client { }
// test
_ , err := client . SendRequest ( "GET" , testURL , nil , nil , nil )
// assert
assert . Error ( t , err )
2021-03-09 14:41:07 +02:00
assert . Contains ( t , err . Error ( ) , "connection" )
assert . Contains ( t , err . Error ( ) , "refused" )
2021-02-04 15:58:35 +02:00
assert . Equal ( t , 0 , httpmock . GetTotalCallCount ( ) , "unexpected number of requests" )
} )
}
2019-12-09 18:35:31 +02:00
func TestSendRequest ( t * testing . T ) {
var passedHeaders = map [ string ] [ ] string { }
passedCookies := [ ] * http . Cookie { }
var passedUsername string
var passedPassword string
2022-10-10 10:55:21 +02:00
2019-12-09 18:35:31 +02:00
// Start a local HTTP server
server := httptest . NewServer ( http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
passedHeaders = map [ string ] [ ] string { }
if req . Header != nil {
for name , headers := range req . Header {
passedHeaders [ name ] = headers
}
}
passedCookies = req . Cookies ( )
passedUsername , passedPassword , _ = req . BasicAuth ( )
rw . Write ( [ ] byte ( "OK" ) )
} ) )
// Close the server when test finishes
defer server . Close ( )
2021-01-04 11:06:28 +02:00
oldLogLevel := logrus . GetLevel ( )
defer logrus . SetLevel ( oldLogLevel )
logrus . SetLevel ( logrus . DebugLevel )
2019-12-09 18:35:31 +02:00
tt := [ ] struct {
client Client
method string
body io . Reader
header http . Header
cookies [ ] * http . Cookie
expected string
} {
2020-01-14 11:29:50 +02:00
{ client : Client { logger : log . Entry ( ) . WithField ( "package" , "SAP/jenkins-library/pkg/http" ) } , method : "GET" , expected : "OK" } ,
2020-09-24 08:58:53 +02:00
{ client : Client { logger : log . Entry ( ) . WithField ( "package" , "SAP/jenkins-library/pkg/http" ) } , method : "GET" , header : map [ string ] [ ] string { "Testheader" : { "Test1" , "Test2" } } , expected : "OK" } ,
2020-01-14 11:29:50 +02:00
{ client : Client { logger : log . Entry ( ) . WithField ( "package" , "SAP/jenkins-library/pkg/http" ) } , cookies : [ ] * http . Cookie { { Name : "TestCookie1" , Value : "TestValue1" } , { Name : "TestCookie2" , Value : "TestValue2" } } , method : "GET" , expected : "OK" } ,
{ client : Client { logger : log . Entry ( ) . WithField ( "package" , "SAP/jenkins-library/pkg/http" ) , username : "TestUser" , password : "TestPwd" } , method : "GET" , expected : "OK" } ,
2022-10-10 10:55:21 +02:00
{ client : Client { logger : log . Entry ( ) . WithField ( "package" , "SAP/jenkins-library/pkg/http" ) , token : "api-token-string" } , method : "GET" , expected : "OK" } ,
2019-12-09 18:35:31 +02:00
}
for key , test := range tt {
t . Run ( fmt . Sprintf ( "Row %v" , key + 1 ) , func ( t * testing . T ) {
2021-01-04 11:06:28 +02:00
oldLogOutput := test . client . logger . Logger . Out
defer func ( ) { test . client . logger . Logger . Out = oldLogOutput } ( )
logBuffer := new ( bytes . Buffer )
test . client . logger . Logger . Out = logBuffer
2019-12-09 18:35:31 +02:00
response , err := test . client . SendRequest ( "GET" , server . URL , test . body , test . header , test . cookies )
2020-09-24 07:41:06 +02:00
assert . NoError ( t , err , "Error occurred but none expected" )
2019-12-09 18:35:31 +02:00
content , err := ioutil . ReadAll ( response . Body )
assert . Equal ( t , test . expected , string ( content ) , "Returned content incorrect" )
response . Body . Close ( )
for k , h := range test . header {
assert . Containsf ( t , passedHeaders , k , "Header %v not contained" , k )
assert . Equalf ( t , h , passedHeaders [ k ] , "Header %v contains different value" )
}
if len ( test . cookies ) > 0 {
assert . Equal ( t , test . cookies , passedCookies , "Passed cookies not correct" )
}
2021-01-04 11:06:28 +02:00
if len ( test . client . username ) > 0 || len ( test . client . password ) > 0 {
if len ( test . client . username ) == 0 || len ( test . client . password ) == 0 {
//"User and password must both be provided"
t . Fail ( )
}
2019-12-09 18:35:31 +02:00
assert . Equal ( t , test . client . username , passedUsername )
assert . Equal ( t , test . client . password , passedPassword )
2021-01-04 11:06:28 +02:00
log := fmt . Sprintf ( "%s" , logBuffer )
credentialsEncoded := base64 . StdEncoding . EncodeToString ( [ ] byte ( fmt . Sprintf ( "%s:%s" , test . client . username , test . client . password ) ) )
assert . NotContains ( t , log , fmt . Sprintf ( "Authorization:[Basic %s]" , credentialsEncoded ) )
assert . Contains ( t , log , "Authorization:[<set>]" )
2019-12-09 18:35:31 +02:00
}
2022-10-10 10:55:21 +02:00
// Token authentication
if len ( test . client . token ) > 0 {
assert . Equal ( t , test . client . token , "api-token-string" )
log := fmt . Sprintf ( "%s" , logBuffer )
assert . Contains ( t , log , fmt . Sprintf ( "Using Token Authentication ****" ) )
assert . Contains ( t , log , "Authorization:[<set>]" )
}
2019-12-09 18:35:31 +02:00
} )
}
}
func TestSetOptions ( t * testing . T ) {
c := Client { }
[TMS] Reimplement tmsUpload step in Go (#3399)
* Initially generated tmsUpload<...> files
* First provisioning of parameters supported by tmsUpload step
* Refer to Go step from tmsUpload.groovy
* Initial client implementation
* Reverting line delimiters in tmsUpoad.groovy back to Unix ones
* Temporarily remove when-condition for Release stage
* Define useGoStep parameter in tmsUpload.groovy
* Unstash buildResult if useGoStep is true
* No unstashing and empty credentials, when using go step
* Register TmsUploadCommand in piper.go
* Cleanup groovy-related changes - they will be temporarily implemented in a different repo
* Make getting OAuth token success
* Look through the code and cleanup it a bit
* Read service key from Jenkins credentials store
* Provide initial set of unit tests for methods in /pkg/tms/tms.go file
* Minor improvements on logging response on http call error
* Check, if positive HTTP status code is as expected
* Cleanup tms.yaml file, provide additional unit test for tms.go
* Provide unit test for the case, when request body contains spaces
* Specify nodeExtDescriptorMapping parameter as of type map in tms.yaml
* Implement client method for getting nodes
* Write tests for GetNodes method
* Add GetMtaExtDescriptor client method and cover it with unit tests
* Provide first implementation for Update- and UploadMtaExtDescriptor
client methods
* Provide first implementation for Update- and UploadMtaExtDescriptor
client methods
* Provide UploadFile and UploadFileToNode client methods
* Provide tests for Update- and UploadMtaExtDescriptor client methods
* Write tests for FileUpload and FileUploadToNode client methods
* Minor corrections
* Remove some TODO comments
* Rename some of response structures
* Revert change for line delimiters in cmd/piper.go
* Add uploadType string parameter to UploadFile and UploadRequest methods
of uploader mock to reflect the changed Uploader implementation
* Start to implement execution logic in tmsUpload.go
* Changes in tms.yaml file
- remove resources from inputs in tms.yaml and implement mtaPath
parameter settings in the yaml file the same way, as it is done in
cloudFoundryDeploy.yaml
- rename tms.yaml to tmsUpload.yaml, since some generation policy
changed meanwhile
* Rename tms.yaml to tmsUpload.yaml and do go generate
* Use provided proxy on communication with UAA and TMS
* Set proxy even before getting OAuth token
* Further implementation of tmsUpload.go
* Continuation on implementing the tmsUpload.go executor
* Get mtarFilePath and git commitId from commonPipelineEnvironment, if
they are missing in configuration file + minor changes
* Implement a happy path test for tmsUpload logic
* Cover with unit tests main happy and error paths of tmsUpload.go logic
* Extend set of unit tests for tmsUpload.go
* Eliminate some TODOs, extend unit tests for tmsUpload.go
* Delete some TODOs
* Remove a couple of more TODOs from tms_test.go file
* Provide additional unit test for error due unexpected positive http
status code on upload
* Revert back line delimiters in cmd/piper.go
* Comment out file uploading calls in tmsUpload.go
* Run go generate to update generated files
* Convert line delimiters in tmsUpload.yaml to Unix ones, as well as
provide new line character in the end of the file, plus minor fix for
logging in tmsUpload.go file (pipeline complained)
* Correct description of a parameter in tmsUpload.yaml, extend unit tests
to check for trimming a slash in the end of TMS url for client methods
that do upload
* [minor] Add a comment in the test code
* Add stashContent parameter to do unstashing in tmsUpload.groovy, remove
some of the clarified TODOs
* Uncomment uploading file calls in tmsUpload.go, declare buildResult
stash in tmsUpload.yaml
* Remove clarified TODOs from the tmsUpload.go file
* Run go fmt for jenkins-library/pkg/tms
* Do not get explicitly values from common pipeline environment - all
configurations are provided in yaml file
* Remove unused struct from tmsUpload_test.go
* Run go fmt jenkins-library\pkg\tms
* Revise descriptions of parameters provided in tmsUpload.yaml file
* Specify STAGES scope for tmsUpload parameters
* Provide STAGES scope for the tmsUpload parameters, provide default value
for stashContent parameter
* Remove trailing space from tmsUpload.yaml
* Provide unit tests for proxy-related changes in http.go file
* Improve proxy implementation in tmsUpload.go file
* Make tmsServiceKey again a mandatory parameter
* Run go generate command to make the generated files correspond the yaml
state
* Change line delimiters back to Unix ones (were switched while resolving
the conflicts)
* Remove trailing spaces from tmsUpload.yaml
* Minor change in a comment to trigger pipelines with commit
* Improve checks for zero-structs and for empty maps, as well as use
different package to read files in the tests
* Revert line endings in http.go
* Revert comments formatting changes in files that do not belong to the tmsUpload step
2022-08-30 10:16:09 +02:00
transportProxy , _ := url . Parse ( "https://proxy.dummy.sap.com" )
opts := ClientOptions { MaxRetries : - 1 , TransportTimeout : 10 , TransportProxy : transportProxy , MaxRequestDuration : 5 , Username : "TestUser" , Password : "TestPassword" , Token : "TestToken" , Logger : log . Entry ( ) . WithField ( "package" , "github.com/SAP/jenkins-library/pkg/http" ) }
2019-12-09 18:35:31 +02:00
c . SetOptions ( opts )
2020-03-23 16:02:22 +02:00
assert . Equal ( t , opts . TransportTimeout , c . transportTimeout )
[TMS] Reimplement tmsUpload step in Go (#3399)
* Initially generated tmsUpload<...> files
* First provisioning of parameters supported by tmsUpload step
* Refer to Go step from tmsUpload.groovy
* Initial client implementation
* Reverting line delimiters in tmsUpoad.groovy back to Unix ones
* Temporarily remove when-condition for Release stage
* Define useGoStep parameter in tmsUpload.groovy
* Unstash buildResult if useGoStep is true
* No unstashing and empty credentials, when using go step
* Register TmsUploadCommand in piper.go
* Cleanup groovy-related changes - they will be temporarily implemented in a different repo
* Make getting OAuth token success
* Look through the code and cleanup it a bit
* Read service key from Jenkins credentials store
* Provide initial set of unit tests for methods in /pkg/tms/tms.go file
* Minor improvements on logging response on http call error
* Check, if positive HTTP status code is as expected
* Cleanup tms.yaml file, provide additional unit test for tms.go
* Provide unit test for the case, when request body contains spaces
* Specify nodeExtDescriptorMapping parameter as of type map in tms.yaml
* Implement client method for getting nodes
* Write tests for GetNodes method
* Add GetMtaExtDescriptor client method and cover it with unit tests
* Provide first implementation for Update- and UploadMtaExtDescriptor
client methods
* Provide first implementation for Update- and UploadMtaExtDescriptor
client methods
* Provide UploadFile and UploadFileToNode client methods
* Provide tests for Update- and UploadMtaExtDescriptor client methods
* Write tests for FileUpload and FileUploadToNode client methods
* Minor corrections
* Remove some TODO comments
* Rename some of response structures
* Revert change for line delimiters in cmd/piper.go
* Add uploadType string parameter to UploadFile and UploadRequest methods
of uploader mock to reflect the changed Uploader implementation
* Start to implement execution logic in tmsUpload.go
* Changes in tms.yaml file
- remove resources from inputs in tms.yaml and implement mtaPath
parameter settings in the yaml file the same way, as it is done in
cloudFoundryDeploy.yaml
- rename tms.yaml to tmsUpload.yaml, since some generation policy
changed meanwhile
* Rename tms.yaml to tmsUpload.yaml and do go generate
* Use provided proxy on communication with UAA and TMS
* Set proxy even before getting OAuth token
* Further implementation of tmsUpload.go
* Continuation on implementing the tmsUpload.go executor
* Get mtarFilePath and git commitId from commonPipelineEnvironment, if
they are missing in configuration file + minor changes
* Implement a happy path test for tmsUpload logic
* Cover with unit tests main happy and error paths of tmsUpload.go logic
* Extend set of unit tests for tmsUpload.go
* Eliminate some TODOs, extend unit tests for tmsUpload.go
* Delete some TODOs
* Remove a couple of more TODOs from tms_test.go file
* Provide additional unit test for error due unexpected positive http
status code on upload
* Revert back line delimiters in cmd/piper.go
* Comment out file uploading calls in tmsUpload.go
* Run go generate to update generated files
* Convert line delimiters in tmsUpload.yaml to Unix ones, as well as
provide new line character in the end of the file, plus minor fix for
logging in tmsUpload.go file (pipeline complained)
* Correct description of a parameter in tmsUpload.yaml, extend unit tests
to check for trimming a slash in the end of TMS url for client methods
that do upload
* [minor] Add a comment in the test code
* Add stashContent parameter to do unstashing in tmsUpload.groovy, remove
some of the clarified TODOs
* Uncomment uploading file calls in tmsUpload.go, declare buildResult
stash in tmsUpload.yaml
* Remove clarified TODOs from the tmsUpload.go file
* Run go fmt for jenkins-library/pkg/tms
* Do not get explicitly values from common pipeline environment - all
configurations are provided in yaml file
* Remove unused struct from tmsUpload_test.go
* Run go fmt jenkins-library\pkg\tms
* Revise descriptions of parameters provided in tmsUpload.yaml file
* Specify STAGES scope for tmsUpload parameters
* Provide STAGES scope for the tmsUpload parameters, provide default value
for stashContent parameter
* Remove trailing space from tmsUpload.yaml
* Provide unit tests for proxy-related changes in http.go file
* Improve proxy implementation in tmsUpload.go file
* Make tmsServiceKey again a mandatory parameter
* Run go generate command to make the generated files correspond the yaml
state
* Change line delimiters back to Unix ones (were switched while resolving
the conflicts)
* Remove trailing spaces from tmsUpload.yaml
* Minor change in a comment to trigger pipelines with commit
* Improve checks for zero-structs and for empty maps, as well as use
different package to read files in the tests
* Revert line endings in http.go
* Revert comments formatting changes in files that do not belong to the tmsUpload step
2022-08-30 10:16:09 +02:00
assert . Equal ( t , opts . TransportProxy , c . transportProxy )
2020-11-09 12:47:03 +02:00
assert . Equal ( t , opts . TransportSkipVerification , c . transportSkipVerification )
2020-03-23 16:02:22 +02:00
assert . Equal ( t , opts . MaxRequestDuration , c . maxRequestDuration )
2019-12-09 18:35:31 +02:00
assert . Equal ( t , opts . Username , c . username )
assert . Equal ( t , opts . Password , c . password )
assert . Equal ( t , opts . Token , c . token )
}
func TestApplyDefaults ( t * testing . T ) {
tt := [ ] struct {
client Client
expected Client
} {
2020-06-10 11:14:55 +02:00
{ client : Client { } , expected : Client { transportTimeout : 3 * time . Minute , maxRequestDuration : 0 , logger : log . Entry ( ) . WithField ( "package" , "SAP/jenkins-library/pkg/http" ) } } ,
2020-03-23 16:02:22 +02:00
{ client : Client { transportTimeout : 10 , maxRequestDuration : 5 } , expected : Client { transportTimeout : 10 , maxRequestDuration : 5 , logger : log . Entry ( ) . WithField ( "package" , "SAP/jenkins-library/pkg/http" ) } } ,
2019-12-09 18:35:31 +02:00
}
for k , v := range tt {
v . client . applyDefaults ( )
assert . Equal ( t , v . expected , v . client , fmt . Sprintf ( "Run %v failed" , k ) )
}
}
2020-01-14 11:29:50 +02:00
2020-01-22 16:10:40 +02:00
func TestUploadRequest ( t * testing . T ) {
2020-01-14 11:29:50 +02:00
var passedHeaders = map [ string ] [ ] string { }
passedCookies := [ ] * http . Cookie { }
var passedUsername string
var passedPassword string
var multipartFile multipart . File
var multipartHeader * multipart . FileHeader
var passedFileContents [ ] byte
// Start a local HTTP server
server := httptest . NewServer ( http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
passedHeaders = map [ string ] [ ] string { }
if req . Header != nil {
for name , headers := range req . Header {
passedHeaders [ name ] = headers
}
}
passedCookies = req . Cookies ( )
passedUsername , passedPassword , _ = req . BasicAuth ( )
err := req . ParseMultipartForm ( 4096 )
if err != nil {
t . FailNow ( )
}
multipartFile , multipartHeader , err = req . FormFile ( "Field1" )
if err != nil {
t . FailNow ( )
}
defer req . Body . Close ( )
passedFileContents , err = ioutil . ReadAll ( multipartFile )
if err != nil {
t . FailNow ( )
}
rw . Write ( [ ] byte ( "OK" ) )
} ) )
// Close the server when test finishes
defer server . Close ( )
testFile , err := ioutil . TempFile ( "" , "testFileUpload" )
if err != nil {
t . FailNow ( )
}
defer os . RemoveAll ( testFile . Name ( ) ) // clean up
fileContents , err := ioutil . ReadFile ( testFile . Name ( ) )
if err != nil {
t . FailNow ( )
}
tt := [ ] struct {
clientOptions ClientOptions
method string
body io . Reader
header http . Header
cookies [ ] * http . Cookie
expected string
} {
2021-06-15 11:13:24 +02:00
{ clientOptions : ClientOptions { MaxRetries : - 1 } , method : "PUT" , expected : "OK" } ,
{ clientOptions : ClientOptions { MaxRetries : - 1 } , method : "POST" , expected : "OK" } ,
{ clientOptions : ClientOptions { MaxRetries : - 1 } , method : "POST" , header : map [ string ] [ ] string { "Testheader" : { "Test1" , "Test2" } } , expected : "OK" } ,
{ clientOptions : ClientOptions { MaxRetries : - 1 } , cookies : [ ] * http . Cookie { { Name : "TestCookie1" , Value : "TestValue1" } , { Name : "TestCookie2" , Value : "TestValue2" } } , method : "POST" , expected : "OK" } ,
{ clientOptions : ClientOptions { MaxRetries : - 1 , Username : "TestUser" , Password : "TestPwd" } , method : "POST" , expected : "OK" } ,
{ clientOptions : ClientOptions { MaxRetries : - 1 , Username : "UserOnly" , Password : "" } , method : "POST" , expected : "OK" } ,
2020-01-14 11:29:50 +02:00
}
client := Client { logger : log . Entry ( ) . WithField ( "package" , "SAP/jenkins-library/pkg/http" ) }
for key , test := range tt {
2020-01-22 15:22:04 +02:00
t . Run ( fmt . Sprintf ( "UploadFile Row %v" , key + 1 ) , func ( t * testing . T ) {
2020-01-14 11:29:50 +02:00
client . SetOptions ( test . clientOptions )
2021-10-21 10:03:42 +02:00
response , err := client . UploadFile ( server . URL , testFile . Name ( ) , "Field1" , test . header , test . cookies , "form" )
2020-01-14 11:29:50 +02:00
assert . NoError ( t , err , "Error occurred but none expected" )
content , err := ioutil . ReadAll ( response . Body )
assert . NoError ( t , err , "Error occurred but none expected" )
assert . Equal ( t , test . expected , string ( content ) , "Returned content incorrect" )
response . Body . Close ( )
2022-03-07 18:15:36 +02:00
assert . Equal ( t , filepath . Base ( testFile . Name ( ) ) , multipartHeader . Filename , "Uploaded file incorrect" )
2020-01-14 11:29:50 +02:00
assert . Equal ( t , fileContents , passedFileContents , "Uploaded file incorrect" )
for k , h := range test . header {
assert . Containsf ( t , passedHeaders , k , "Header %v not contained" , k )
assert . Equalf ( t , h , passedHeaders [ k ] , "Header %v contains different value" )
}
if len ( test . cookies ) > 0 {
assert . Equal ( t , test . cookies , passedCookies , "Passed cookies not correct" )
}
if len ( client . username ) > 0 {
assert . Equal ( t , client . username , passedUsername )
}
2020-01-22 15:22:04 +02:00
if len ( client . password ) > 0 {
assert . Equal ( t , client . password , passedPassword )
}
} )
t . Run ( fmt . Sprintf ( "UploadRequest Row %v" , key + 1 ) , func ( t * testing . T ) {
client . SetOptions ( test . clientOptions )
2021-10-21 10:03:42 +02:00
response , err := client . UploadRequest ( test . method , server . URL , testFile . Name ( ) , "Field1" , test . header , test . cookies , "form" )
2020-01-22 15:22:04 +02:00
assert . NoError ( t , err , "Error occurred but none expected" )
content , err := ioutil . ReadAll ( response . Body )
assert . NoError ( t , err , "Error occurred but none expected" )
assert . Equal ( t , test . expected , string ( content ) , "Returned content incorrect" )
response . Body . Close ( )
2022-03-07 18:15:36 +02:00
assert . Equal ( t , filepath . Base ( testFile . Name ( ) ) , multipartHeader . Filename , "Uploaded file incorrect" )
2020-01-22 15:22:04 +02:00
assert . Equal ( t , fileContents , passedFileContents , "Uploaded file incorrect" )
for k , h := range test . header {
assert . Containsf ( t , passedHeaders , k , "Header %v not contained" , k )
assert . Equalf ( t , h , passedHeaders [ k ] , "Header %v contains different value" )
}
if len ( test . cookies ) > 0 {
assert . Equal ( t , test . cookies , passedCookies , "Passed cookies not correct" )
}
if len ( client . username ) > 0 {
assert . Equal ( t , client . username , passedUsername )
}
2020-01-14 11:29:50 +02:00
if len ( client . password ) > 0 {
assert . Equal ( t , client . password , passedPassword )
}
} )
}
}
2020-01-22 16:10:40 +02:00
2020-03-23 16:02:22 +02:00
func TestUploadRequestWrongMethod ( t * testing . T ) {
2020-01-22 16:10:40 +02:00
client := Client { logger : log . Entry ( ) . WithField ( "package" , "SAP/jenkins-library/pkg/http" ) }
2021-10-21 10:03:42 +02:00
_ , err := client . UploadRequest ( "GET" , "dummy" , "testFile" , "Field1" , nil , nil , "form" )
2020-09-24 07:41:06 +02:00
assert . Error ( t , err , "No error occurred but was expected" )
2020-01-22 16:10:40 +02:00
}
2020-03-23 16:02:22 +02:00
func TestTransportTimout ( t * testing . T ) {
t . Run ( "timeout works on transport level" , func ( t * testing . T ) {
// init
svr := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
// Sleep for longer than the configured timeout
time . Sleep ( 2 * time . Second )
} ) )
defer svr . Close ( )
client := Client { transportTimeout : 1 * time . Second }
buffer := bytes . Buffer { }
// test
_ , err := client . SendRequest ( http . MethodGet , svr . URL , & buffer , nil , nil )
// assert
2020-03-31 09:18:09 +02:00
if assert . Error ( t , err , "expected request to fail" ) {
assert . Contains ( t , err . Error ( ) , "timeout awaiting response headers" )
}
2020-03-23 16:02:22 +02:00
} )
t . Run ( "timeout is not hit on transport level" , func ( t * testing . T ) {
// init
svr := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
// Sleep for less than the configured timeout
time . Sleep ( 1 * time . Second )
} ) )
defer svr . Close ( )
client := Client { transportTimeout : 2 * time . Second }
buffer := bytes . Buffer { }
// test
_ , err := client . SendRequest ( http . MethodGet , svr . URL , & buffer , nil , nil )
// assert
assert . NoError ( t , err )
} )
}
2020-07-14 10:58:57 +02:00
2020-11-09 12:47:03 +02:00
func TestTransportSkipVerification ( t * testing . T ) {
testCases := [ ] struct {
client Client
expectedError string
} {
{ client : Client { } , expectedError : "certificate signed by unknown authority" } ,
{ client : Client { transportSkipVerification : false } , expectedError : "certificate signed by unknown authority" } ,
{ client : Client { transportSkipVerification : true } } ,
}
for _ , testCase := range testCases {
// init
svr := httptest . NewTLSServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) { } ) )
defer svr . Close ( )
// test
_ , err := testCase . client . SendRequest ( http . MethodGet , svr . URL , & bytes . Buffer { } , nil , nil )
// assert
if len ( testCase . expectedError ) > 0 {
assert . Error ( t , err , "certificate signed by unknown authority" )
} else {
assert . NoError ( t , err )
}
}
}
2021-08-19 11:29:33 +02:00
func TestTransportWithCertifacteAdded ( t * testing . T ) {
server := httptest . NewTLSServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
io . WriteString ( w , "Hello" )
} ) )
defer server . Close ( )
certs := x509 . NewCertPool ( )
for _ , c := range server . TLS . Certificates {
roots , err := x509 . ParseCertificates ( c . Certificate [ len ( c . Certificate ) - 1 ] )
if err != nil {
println ( "error parsing server's root cert: %v" , err )
}
for _ , root := range roots {
certs . AddCert ( root )
}
}
client := http . Client { Transport : & http . Transport { TLSClientConfig : & tls . Config { RootCAs : certs , InsecureSkipVerify : false } } }
_ , err := client . Get ( server . URL )
// assert
assert . NoError ( t , err )
}
2020-11-11 14:35:53 +02:00
func TestMaxRetries ( t * testing . T ) {
testCases := [ ] struct {
client Client
countedCalls int
2021-01-22 10:56:56 +02:00
method string
responseCode int
errorText string
2021-03-09 14:41:07 +02:00
timeout bool
2020-11-11 14:35:53 +02:00
} {
2021-03-09 14:41:07 +02:00
{ client : Client { maxRetries : 1 , useDefaultTransport : true , transportSkipVerification : true , transportTimeout : 1 * time . Microsecond } , responseCode : 666 , timeout : true , countedCalls : 2 , method : http . MethodPost , errorText : "timeout awaiting response headers" } ,
2021-01-22 10:56:56 +02:00
{ client : Client { maxRetries : 0 } , countedCalls : 1 , method : http . MethodGet , responseCode : 500 , errorText : "Internal Server Error" } ,
{ client : Client { maxRetries : 2 } , countedCalls : 3 , method : http . MethodGet , responseCode : 500 , errorText : "Internal Server Error" } ,
{ client : Client { maxRetries : 3 } , countedCalls : 4 , method : http . MethodPost , responseCode : 503 , errorText : "Service Unavailable" } ,
2021-03-09 14:41:07 +02:00
{ client : Client { maxRetries : 1 } , countedCalls : 2 , method : http . MethodPut , responseCode : 506 , errorText : "Variant Also Negotiates" } ,
{ client : Client { maxRetries : 1 } , countedCalls : 2 , method : http . MethodHead , responseCode : 502 , errorText : "Bad Gateway" } ,
2021-01-22 10:56:56 +02:00
{ client : Client { maxRetries : 3 } , countedCalls : 1 , method : http . MethodHead , responseCode : 404 , errorText : "Not Found" } ,
{ client : Client { maxRetries : 3 } , countedCalls : 1 , method : http . MethodHead , responseCode : 401 , errorText : "Authentication Error" } ,
{ client : Client { maxRetries : 3 } , countedCalls : 1 , method : http . MethodHead , responseCode : 403 , errorText : "Authorization Error" } ,
2020-11-11 14:35:53 +02:00
}
for _ , testCase := range testCases {
// init
count := 0
svr := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
count ++
2021-03-09 14:41:07 +02:00
if testCase . timeout && count == 0 {
time . Sleep ( 3 * time . Microsecond )
}
2021-01-22 10:56:56 +02:00
w . WriteHeader ( testCase . responseCode )
2020-11-11 14:35:53 +02:00
} ) )
defer svr . Close ( )
// test
2021-01-22 10:56:56 +02:00
_ , err := testCase . client . SendRequest ( testCase . method , svr . URL , & bytes . Buffer { } , nil , nil )
2020-11-11 14:35:53 +02:00
// assert
2021-01-22 10:56:56 +02:00
assert . Error ( t , err , fmt . Sprintf ( "%v: %v" , testCase . errorText , "Expected error but did not detect one" ) )
assert . Equal ( t , testCase . countedCalls , count , fmt . Sprintf ( "%v: %v" , testCase . errorText , "Number of invocations mismatch" ) )
2020-11-11 14:35:53 +02:00
}
}
2020-07-14 10:58:57 +02:00
func TestParseHTTPResponseBodyJSON ( t * testing . T ) {
type myJSONStruct struct {
FullName string ` json:"full_name" `
Name string ` json:"name" `
Owner struct {
Login string ` json:"login" `
} ` json:"owner" `
}
2020-09-24 07:41:06 +02:00
t . Run ( "parse JSON successful" , func ( t * testing . T ) {
2020-07-14 10:58:57 +02:00
json := ` { "name":"Test Name","full_name":"test full name","owner": { "login": "octocat"}} `
// create a new reader with that JSON
r := ioutil . NopCloser ( bytes . NewReader ( [ ] byte ( json ) ) )
httpResponse := http . Response {
StatusCode : 200 ,
Body : r ,
}
var response myJSONStruct
err := ParseHTTPResponseBodyJSON ( & httpResponse , & response )
if assert . NoError ( t , err ) {
t . Run ( "check correct parsing" , func ( t * testing . T ) {
assert . Equal ( t , myJSONStruct ( myJSONStruct { FullName : "test full name" , Name : "Test Name" , Owner : struct {
Login string "json:\"login\""
} { Login : "octocat" } } ) , response )
} )
}
} )
t . Run ( "http response is nil" , func ( t * testing . T ) {
var response myJSONStruct
err := ParseHTTPResponseBodyJSON ( nil , & response )
t . Run ( "check error" , func ( t * testing . T ) {
assert . EqualError ( t , err , "cannot parse HTTP response with value <nil>" )
} )
} )
t . Run ( "wrong JSON formatting" , func ( t * testing . T ) {
json := ` { "name":"Test Name","full_name":"test full name";"owner": { "login": "octocat"}} `
r := ioutil . NopCloser ( bytes . NewReader ( [ ] byte ( json ) ) )
httpResponse := http . Response {
StatusCode : 200 ,
Body : r ,
}
var response myJSONStruct
err := ParseHTTPResponseBodyJSON ( & httpResponse , & response )
println ( response . FullName )
t . Run ( "check error" , func ( t * testing . T ) {
assert . EqualError ( t , err , "HTTP response body could not be parsed as JSON: {\"name\":\"Test Name\",\"full_name\":\"test full name\";\"owner\":{\"login\": \"octocat\"}}: invalid character ';' after object key:value pair" )
} )
} )
t . Run ( "IO read error" , func ( t * testing . T ) {
mockReadCloser := mockReadCloser { }
// if Read is called, it will return error
mockReadCloser . On ( "Read" , mock . AnythingOfType ( "[]uint8" ) ) . Return ( 0 , fmt . Errorf ( "error reading" ) )
// if Close is called, it will return error
mockReadCloser . On ( "Close" ) . Return ( fmt . Errorf ( "error closing" ) )
httpResponse := http . Response {
StatusCode : 200 ,
Body : & mockReadCloser ,
}
var response myJSONStruct
err := ParseHTTPResponseBodyJSON ( & httpResponse , & response )
t . Run ( "check error" , func ( t * testing . T ) {
assert . EqualError ( t , err , "HTTP response body could not be read: error reading" )
} )
} )
}
func TestParseHTTPResponseBodyXML ( t * testing . T ) {
type myXMLStruct struct {
XMLName xml . Name ` xml:"service" `
Text string ` xml:",chardata" `
App string ` xml:"app,attr" `
Atom string ` xml:"atom,attr" `
}
2020-09-24 07:41:06 +02:00
t . Run ( "parse XML successful" , func ( t * testing . T ) {
2020-07-14 10:58:57 +02:00
myXML := `
< ? xml version = "1.0" encoding = "utf-8" ? >
< app : service xmlns : app = "http://www.w3.org/2007/app" xmlns : atom = "http://www.w3.org/2005/Atom" / >
`
// create a new reader with that xml
r := ioutil . NopCloser ( bytes . NewReader ( [ ] byte ( myXML ) ) )
httpResponse := http . Response {
StatusCode : 200 ,
Body : r ,
}
var response myXMLStruct
err := ParseHTTPResponseBodyXML ( & httpResponse , & response )
if assert . NoError ( t , err ) {
t . Run ( "check correct parsing" , func ( t * testing . T ) {
// assert.Equal(t, "<?xml version=\"1.0\" encoding=\"utf-8\"?><app:service xmlns:app=\"http://www.w3.org/2007/app\" xmlns:atom=\"http://www.w3.org/2005/Atom\"/>", response)
assert . Equal ( t , myXMLStruct ( myXMLStruct { XMLName : xml . Name { Space : "http://www.w3.org/2007/app" , Local : "service" } , Text : "" , App : "http://www.w3.org/2007/app" , Atom : "http://www.w3.org/2005/Atom" } ) , response )
} )
}
} )
t . Run ( "http response is nil" , func ( t * testing . T ) {
var response myXMLStruct
err := ParseHTTPResponseBodyXML ( nil , & response )
t . Run ( "check error" , func ( t * testing . T ) {
assert . EqualError ( t , err , "cannot parse HTTP response with value <nil>" )
} )
} )
t . Run ( "wrong XML formatting" , func ( t * testing . T ) {
myXML := `
< ? xml version = "1.0" encoding = "utf-8" ? >
< app : service xmlns : app = http : //www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom"/>
`
r := ioutil . NopCloser ( bytes . NewReader ( [ ] byte ( myXML ) ) )
httpResponse := http . Response {
StatusCode : 200 ,
Body : r ,
}
var response myXMLStruct
err := ParseHTTPResponseBodyXML ( & httpResponse , & response )
t . Run ( "check error" , func ( t * testing . T ) {
assert . EqualError ( t , err , "HTTP response body could not be parsed as XML: \n\t\t<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\t\t<app:service xmlns:app=http://www.w3.org/2007/app\" xmlns:atom=\"http://www.w3.org/2005/Atom\"/>\n\t\t: XML syntax error on line 3: unquoted or missing attribute value in element" )
} )
} )
t . Run ( "IO read error" , func ( t * testing . T ) {
mockReadCloser := mockReadCloser { }
// if Read is called, it will return error
mockReadCloser . On ( "Read" , mock . AnythingOfType ( "[]uint8" ) ) . Return ( 0 , fmt . Errorf ( "error reading" ) )
// if Close is called, it will return error
mockReadCloser . On ( "Close" ) . Return ( fmt . Errorf ( "error closing" ) )
httpResponse := http . Response {
StatusCode : 200 ,
Body : & mockReadCloser ,
}
var response myXMLStruct
err := ParseHTTPResponseBodyXML ( & httpResponse , & response )
t . Run ( "check error" , func ( t * testing . T ) {
assert . EqualError ( t , err , "HTTP response body could not be read: error reading" )
} )
} )
}
type mockReadCloser struct {
mock . Mock
}
func ( m * mockReadCloser ) Read ( p [ ] byte ) ( n int , err error ) {
args := m . Called ( p )
return args . Int ( 0 ) , args . Error ( 1 )
}
func ( m * mockReadCloser ) Close ( ) error {
args := m . Called ( )
return args . Error ( 0 )
}