2019-08-07 16:02:13 +00:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
2019-08-24 20:45:20 +00:00
|
|
|
"bytes"
|
2019-08-07 16:02:13 +00:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2019-08-24 04:39:43 +00:00
|
|
|
"io"
|
2019-08-07 16:02:13 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
2019-08-09 16:39:40 +00:00
|
|
|
"net/url"
|
2019-08-07 16:02:13 +00:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
)
|
|
|
|
|
2019-08-23 16:11:03 +00:00
|
|
|
func readRequestBodyAsJSON(req *http.Request, body *map[string]interface{}) (err error) {
|
2019-08-07 16:02:13 +00:00
|
|
|
bodyBytes, err := ioutil.ReadAll(req.Body)
|
|
|
|
defer req.Body.Close()
|
|
|
|
err = json.Unmarshal(bodyBytes, body)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-08-23 16:11:03 +00:00
|
|
|
func writeResponseBodyAsJSON(w http.ResponseWriter, body map[string]interface{}) (err error) {
|
2019-08-07 16:02:13 +00:00
|
|
|
bodyBytes, err := json.Marshal(body)
|
|
|
|
fmt.Fprintln(w, string(bodyBytes))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-08-07 16:02:41 +00:00
|
|
|
func TestNewClient(t *testing.T) {
|
2019-08-23 16:11:03 +00:00
|
|
|
apiURL, _ := url.Parse("http://validurl.com/api")
|
2019-08-09 16:39:40 +00:00
|
|
|
|
|
|
|
validClient := NewClient(http.DefaultClient, Config{
|
2019-08-23 16:11:03 +00:00
|
|
|
URL: apiURL,
|
2019-08-07 16:02:41 +00:00
|
|
|
})
|
|
|
|
assert.NotNil(t, validClient)
|
|
|
|
}
|
|
|
|
|
2019-08-07 16:02:13 +00:00
|
|
|
func TestClientAuthenticates(t *testing.T) {
|
|
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
|
|
|
var body map[string]interface{}
|
2019-08-23 16:11:03 +00:00
|
|
|
err := readRequestBodyAsJSON(req, &body)
|
2019-08-07 16:02:13 +00:00
|
|
|
|
|
|
|
assert.Equal(t, req.Method, http.MethodPost)
|
|
|
|
assert.Equal(t, req.RequestURI, "/api/auth")
|
|
|
|
assert.NotNil(t, req.Header["Content-Type"])
|
|
|
|
assert.NotNil(t, req.Header["Content-Type"][0])
|
|
|
|
assert.Equal(t, req.Header["Content-Type"][0], "application/json")
|
2019-08-09 19:16:12 +00:00
|
|
|
assert.NotNil(t, req.Header["User-Agent"])
|
|
|
|
assert.NotNil(t, req.Header["User-Agent"][0])
|
|
|
|
assert.Equal(t, req.Header["User-Agent"][0], "GE007")
|
2019-08-07 16:02:13 +00:00
|
|
|
assert.Nil(t, err)
|
|
|
|
assert.NotNil(t, body["Username"])
|
|
|
|
assert.Equal(t, body["Username"], "admin")
|
|
|
|
assert.NotNil(t, body["Password"])
|
|
|
|
assert.Equal(t, body["Password"], "a")
|
|
|
|
|
2019-08-23 16:11:03 +00:00
|
|
|
writeResponseBodyAsJSON(w, map[string]interface{}{
|
2019-08-07 16:02:13 +00:00
|
|
|
"jwt": "somerandomtoken",
|
|
|
|
})
|
|
|
|
}))
|
|
|
|
defer ts.Close()
|
|
|
|
|
2019-08-23 16:11:03 +00:00
|
|
|
apiURL, _ := url.Parse(ts.URL + "/api/")
|
2019-08-09 16:39:40 +00:00
|
|
|
|
|
|
|
customClient := NewClient(ts.Client(), Config{
|
2019-08-23 16:11:03 +00:00
|
|
|
URL: apiURL,
|
2019-08-09 19:16:12 +00:00
|
|
|
User: "admin",
|
|
|
|
Password: "a",
|
|
|
|
UserAgent: "GE007",
|
2019-08-07 16:02:13 +00:00
|
|
|
})
|
2019-08-26 05:13:43 +00:00
|
|
|
token, err := customClient.Auth()
|
2019-08-07 16:02:13 +00:00
|
|
|
assert.Nil(t, err)
|
|
|
|
assert.Equal(t, token, "somerandomtoken")
|
|
|
|
}
|
2019-08-24 04:39:43 +00:00
|
|
|
|
|
|
|
func Test_portainerClientImp_do(t *testing.T) {
|
|
|
|
type fields struct {
|
|
|
|
user string
|
|
|
|
password string
|
|
|
|
token string
|
|
|
|
userAgent string
|
|
|
|
beforeRequestHooks []func(req *http.Request) (err error)
|
|
|
|
afterResponseHooks []func(resp *http.Response) (err error)
|
|
|
|
server *httptest.Server
|
|
|
|
beforeFunctionCall func(t *testing.T, tt *fields)
|
|
|
|
}
|
|
|
|
type args struct {
|
2019-08-24 19:06:16 +00:00
|
|
|
uri string
|
|
|
|
method string
|
|
|
|
requestBody io.Reader
|
|
|
|
headers http.Header
|
2019-08-24 04:39:43 +00:00
|
|
|
}
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
fields fields
|
|
|
|
args args
|
|
|
|
wantRespCheck func(resp *http.Response) bool
|
|
|
|
wantErr bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "error on bad URI",
|
|
|
|
fields: fields{
|
|
|
|
server: httptest.NewUnstartedServer(nil),
|
|
|
|
},
|
|
|
|
args: args{
|
|
|
|
uri: string(0x7f),
|
|
|
|
},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "error on bad method",
|
|
|
|
fields: fields{
|
|
|
|
server: httptest.NewUnstartedServer(nil),
|
|
|
|
},
|
|
|
|
args: args{
|
|
|
|
method: "WOLOLO?",
|
|
|
|
},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "extra headers are added",
|
|
|
|
fields: fields{
|
|
|
|
server: httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
|
|
|
assert.Equal(t, req.Header.Get("Some-Header"), "value")
|
|
|
|
})),
|
|
|
|
},
|
|
|
|
args: args{
|
|
|
|
headers: http.Header{
|
|
|
|
"Some-Header": []string{
|
|
|
|
"value",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "returns error on http error",
|
|
|
|
fields: fields{
|
|
|
|
token: "token",
|
|
|
|
server: httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {})),
|
|
|
|
beforeFunctionCall: func(t *testing.T, tt *fields) {
|
|
|
|
tt.server.Close()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "returns error on response error",
|
|
|
|
fields: fields{
|
|
|
|
server: httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
})),
|
|
|
|
},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
tt.fields.server.Start()
|
|
|
|
defer tt.fields.server.Close()
|
|
|
|
|
|
|
|
apiURL, _ := url.Parse(tt.fields.server.URL + "/api/")
|
|
|
|
|
|
|
|
n := &portainerClientImp{
|
|
|
|
httpClient: tt.fields.server.Client(),
|
|
|
|
url: apiURL,
|
|
|
|
user: tt.fields.user,
|
|
|
|
password: tt.fields.password,
|
|
|
|
token: tt.fields.token,
|
|
|
|
userAgent: tt.fields.userAgent,
|
|
|
|
beforeRequestHooks: tt.fields.beforeRequestHooks,
|
|
|
|
afterResponseHooks: tt.fields.afterResponseHooks,
|
|
|
|
}
|
|
|
|
|
|
|
|
if tt.fields.beforeFunctionCall != nil {
|
|
|
|
tt.fields.beforeFunctionCall(t, &tt.fields)
|
|
|
|
}
|
2019-08-24 19:06:16 +00:00
|
|
|
gotResp, err := n.do(tt.args.uri, tt.args.method, tt.args.requestBody, tt.args.headers)
|
2019-08-24 04:39:43 +00:00
|
|
|
|
|
|
|
assert.Equal(t, tt.wantErr, err != nil)
|
|
|
|
if tt.wantRespCheck != nil {
|
|
|
|
assert.True(t, tt.wantRespCheck(gotResp))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2019-08-24 20:45:20 +00:00
|
|
|
|
|
|
|
func Test_checkResponseForErrors(t *testing.T) {
|
|
|
|
type args struct {
|
|
|
|
resp *http.Response
|
|
|
|
}
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
args args
|
|
|
|
wantErr bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "generic error",
|
|
|
|
args: args{
|
|
|
|
resp: func() (resp *http.Response) {
|
|
|
|
resp = &http.Response{
|
|
|
|
StatusCode: http.StatusNotFound,
|
|
|
|
}
|
|
|
|
bodyBytes, _ := json.Marshal(map[string]interface{}{
|
|
|
|
"Err": "Error",
|
|
|
|
"Details": "Not found",
|
|
|
|
})
|
|
|
|
resp.Body = ioutil.NopCloser(bytes.NewReader(bodyBytes))
|
|
|
|
return
|
|
|
|
}(),
|
|
|
|
},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "non generic error",
|
|
|
|
args: args{
|
|
|
|
resp: func() (resp *http.Response) {
|
|
|
|
resp = &http.Response{
|
|
|
|
StatusCode: http.StatusNotFound,
|
|
|
|
Body: ioutil.NopCloser(bytes.NewReader([]byte("Err"))),
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}(),
|
|
|
|
},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
assert.Equal(t, tt.wantErr, checkResponseForErrors(tt.args.resp) != nil)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|