2019-12-09 18:35:31 +02:00
package http
import (
2020-03-23 16:02:22 +02:00
"bytes"
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"
2020-01-14 11:29:50 +02:00
"os"
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 { }
client . SetOptions ( ClientOptions { UseDefaultTransport : true } )
// 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 { }
client . SetOptions ( ClientOptions { UseDefaultTransport : true } )
// when
response , err := client . Send ( request )
// then
assert . Error ( t , err )
assert . Nil ( t , response )
} )
}
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 { }
client . SetOptions ( ClientOptions { UseDefaultTransport : true } )
// 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 )
assert . Contains ( t , err . Error ( ) , "connect: connection refused" )
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
// 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" } ,
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
}
} )
}
}
func TestSetOptions ( t * testing . T ) {
c := Client { }
2020-03-23 16:02:22 +02:00
opts := ClientOptions { TransportTimeout : 10 , 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 )
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
} {
2020-01-22 15:22:04 +02:00
{ clientOptions : ClientOptions { } , method : "PUT" , expected : "OK" } ,
2020-01-14 11:29:50 +02:00
{ clientOptions : ClientOptions { } , method : "POST" , expected : "OK" } ,
2020-09-24 08:58:53 +02:00
{ clientOptions : ClientOptions { } , method : "POST" , header : map [ string ] [ ] string { "Testheader" : { "Test1" , "Test2" } } , expected : "OK" } ,
2020-01-14 11:29:50 +02:00
{ clientOptions : ClientOptions { } , cookies : [ ] * http . Cookie { { Name : "TestCookie1" , Value : "TestValue1" } , { Name : "TestCookie2" , Value : "TestValue2" } } , method : "POST" , expected : "OK" } ,
{ clientOptions : ClientOptions { Username : "TestUser" , Password : "TestPwd" } , method : "POST" , expected : "OK" } ,
2020-11-03 20:36:52 +02:00
{ clientOptions : ClientOptions { 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 )
response , err := client . UploadFile ( server . URL , testFile . Name ( ) , "Field1" , test . header , test . cookies )
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 ( )
assert . Equal ( t , testFile . Name ( ) , multipartHeader . Filename , "Uploaded file incorrect" )
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 )
response , err := client . UploadRequest ( test . method , server . URL , testFile . Name ( ) , "Field1" , test . header , test . cookies )
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 ( )
assert . Equal ( t , testFile . Name ( ) , multipartHeader . Filename , "Uploaded file incorrect" )
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" ) }
_ , err := client . UploadRequest ( "GET" , "dummy" , "testFile" , "Field1" , nil , nil )
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 )
}
}
}
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
2020-11-11 14:35:53 +02:00
} {
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" } ,
{ client : Client { maxRetries : 3 } , countedCalls : 4 , method : http . MethodPut , responseCode : 506 , errorText : "Variant Also Negotiates" } ,
{ client : Client { maxRetries : 3 } , countedCalls : 4 , method : http . MethodHead , responseCode : 502 , errorText : "Bad Gateway" } ,
{ 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-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 )
}