-
Notifications
You must be signed in to change notification settings - Fork 14
/
client_test.go
262 lines (215 loc) · 8.7 KB
/
client_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
package gotransip
import (
"bytes"
"errors"
"io"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"testing"
"testing/iotest"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/transip/gotransip/v6/authenticator"
"github.com/transip/gotransip/v6/repository"
"github.com/transip/gotransip/v6/rest"
)
func TestNewClient(t *testing.T) {
var cc ClientConfiguration
var err error
// empty ClientConfig should raise error about missing AccountName
_, err = NewClient(cc)
if assert.Errorf(t, err, "accountname error not returned") {
assert.Equal(t, errors.New("AccountName is required"), err)
}
// ... unless a token is provided
cc.Token = authenticator.DemoToken
_, err = NewClient(cc)
require.NoError(t, err, "No error when correct token is provided")
cc.Token = ""
// no error should be thrown when enabling demo mode
client, err := newClient(DemoClientConfiguration)
require.NoError(t, err, "No error should be thrown upon enabling demo mode")
assert.Equal(t, authenticator.DemoToken, client.GetConfig().Token, "Token should be demo token")
cc.AccountName = "foobar"
// ClientConfig with only AccountName set should raise error about private keys
_, err = NewClient(cc)
if assert.Errorf(t, err, "expecting private key, token required error") {
assert.EqualError(t, err, "PrivateKeyReader, token or PrivateKeyReader is required")
}
cc.PrivateKeyReader = iotest.TimeoutReader(bytes.NewReader([]byte{0, 1}))
_, err = NewClient(cc)
if assert.Errorf(t, err, "expecting private key reader error returned") {
assert.EqualError(t, err, "error while reading private key: timeout")
}
cc.PrivateKeyReader = nil
// Override PrivateKeyBody with PrivateKeyReader
pkBody := []byte{2, 3, 4, 5}
cc.PrivateKeyReader = bytes.NewReader(pkBody)
client, err = newClient(cc)
clientAuthenticator := client.GetAuthenticator()
config := client.GetConfig()
assert.NoError(t, err)
// Test if private key body is passed to the authenticator
assert.Equal(t, pkBody, clientAuthenticator.PrivateKeyBody)
// Test if the base url is passed to the authenticator
assert.Equal(t, config.URL, clientAuthenticator.BasePath)
// Test if the account name is passed on to the authenticator
assert.Equal(t, "foobar", clientAuthenticator.Login)
// Test if private key from path is read and passed to the authenticator
privateKeyFile, err := os.Open("testdata/signature.key")
require.NoError(t, err)
privateKeyBody, err := io.ReadAll(privateKeyFile)
require.NoError(t, err)
// Test that a tokencache is passed to the authenticator
cacheFile := filepath.Join(os.TempDir(), "gotransip_test_token_cache")
defer os.Remove(cacheFile)
cache, err := authenticator.NewFileTokenCache(cacheFile)
require.NoError(t, err)
client, err = newClient(ClientConfiguration{PrivateKeyPath: "testdata/signature.key", AccountName: "example-user", TokenCache: cache})
require.NoError(t, err)
clientAuthenticator = client.GetAuthenticator()
require.NotNil(t, clientAuthenticator.TokenCache)
assert.Equal(t, cache, clientAuthenticator.TokenCache)
// Check if private key read from file is the same as the key body on the authenticator
assert.Equal(t, privateKeyBody, clientAuthenticator.PrivateKeyBody)
// Test that the default expiration time is set on the authenticator
assert.Equal(t, time.Duration(0), clientAuthenticator.TokenExpiration)
// Test that the default whitelisted value is set on the authenticator
assert.False(t, clientAuthenticator.Whitelisted)
// Override TokenExpiration to 30 seconds
cc.TokenExpiration = 30 * time.Second
// Override TokenWhitelisted to true
cc.TokenWhitelisted = true
client, err = newClient(cc)
clientAuthenticator = client.GetAuthenticator()
assert.NoError(t, err)
// Test that the new expiration time is set on the authenticator
assert.Equal(t, cc.TokenExpiration, clientAuthenticator.TokenExpiration)
// Test that the new whitelisted value is set on the authenticator
assert.True(t, clientAuthenticator.Whitelisted)
// Also, with no mode set, it should default to APIModeReadWrite
assert.Equal(t, APIModeReadWrite, config.Mode)
// Check if the base path is set by default
assert.Equal(t, "https://api.transip.nl/v6", config.URL)
cc.PrivateKeyReader = nil
// Override API mode to APIModeReadOnly
cc.Mode = APIModeReadOnly
cc.Token = authenticator.DemoToken
client, err = newClient(cc)
clientAuthenticator = client.GetAuthenticator()
assert.NoError(t, err)
// Assert that the api mode is set on the authenticator
assert.True(t, clientAuthenticator.ReadOnly)
}
func TestClientCallReturnsObject(t *testing.T) {
apiResponse := `{"domains":[{"name":"testje.nl"}]}`
server := mockServer{t: t, expectedMethod: "GET", expectedURL: "/domains", statusCode: 200, response: apiResponse}
client, tearDown := server.getClient()
defer tearDown()
restRequest := rest.Request{Endpoint: "/domains"}
type domainResponse struct {
Name string `json:"name"`
}
var domainsResponse struct {
Domains []domainResponse `json:"domains"`
}
err := client.Get(restRequest, &domainsResponse)
require.NoError(t, err)
require.Equal(t, 1, len(domainsResponse.Domains))
assert.Equal(t, "testje.nl", domainsResponse.Domains[0].Name)
}
func TestEmptyBodyPostDoesPostWithoutBody(t *testing.T) {
apiResponse := `{"domains":[{"name":"test.nl"}]}`
server := mockServer{t: t, expectedMethod: "POST", expectedURL: "/test", statusCode: 201, response: apiResponse}
client, tearDown := server.getClient()
defer tearDown()
restRequest := rest.Request{Endpoint: "/test"}
err := client.Post(restRequest)
require.NoError(t, err)
}
// Test if we can connect to the api server using the demo token
func TestClient_CallToLiveApiServer(t *testing.T) {
clientConfig := ClientConfiguration{
Token: authenticator.DemoToken,
}
client, err := NewClient(clientConfig)
require.NoError(t, err)
request := rest.Request{Endpoint: "/api-test"}
var responseObject struct {
Response string `json:"ping"`
}
err = client.Get(request, &responseObject)
require.NoError(t, err)
assert.NotZero(t, len(responseObject.Response))
}
func TestClient_TestMode(t *testing.T) {
apiResponse := `{"ping":"pong"}`
params := url.Values{"test": []string{"1"}}
server := mockServer{t: t, expectedMethod: "POST", expectedURL: "/test?test=1", statusCode: 200, response: apiResponse, expectedParams: params}
httpServer := server.getHTTPServer()
defer httpServer.Close()
// setup a client with test mode enabled
clientConfig := DemoClientConfiguration
clientConfig.URL = httpServer.URL
clientConfig.TestMode = true
client, err := NewClient(clientConfig)
require.NoError(t, err)
restRequest := rest.Request{Endpoint: "/test"}
err = client.Post(restRequest)
require.NoError(t, err)
}
// mockServer struct is used to test the how the client sends a request
// and responds to a servers response
type mockServer struct {
t *testing.T
expectedURL string
expectedMethod string
statusCode int
expectedRequest string
response string
expectedParams url.Values
}
func (m *mockServer) getHTTPServer() *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
assert.Equal(m.t, m.expectedURL, req.URL.String()) // check if right expectedURL is called
if req.ContentLength != 0 {
// get the request body
// and check if the body matches the expected request body
body, err := io.ReadAll(req.Body)
require.NoError(m.t, err)
assert.Equal(m.t, m.expectedRequest, string(body))
}
// expect http query strings to be equal
assert.Equal(m.t, m.expectedParams.Encode(), req.URL.RawQuery)
// check if a signature is set
assert.NotEmpty(m.t, req.Header.Get("Authorization"), "Authentication header not set")
// check if the request has the right content-type
assert.Equal(m.t, req.Header.Get("Accept"), "application/json")
// check if the request has the right user-agent
assert.Equal(m.t, req.Header.Get("User-Agent"), userAgent)
// check if the request has the right content-type
assert.Equal(m.t, req.Header.Get("Content-Type"), "application/json")
assert.Equal(m.t, m.expectedMethod, req.Method) // check if the right expectedRequest expectedMethod is used
rw.WriteHeader(m.statusCode) // respond with given status code
if m.response != "" {
_, err := rw.Write([]byte(m.response))
require.NoError(m.t, err, "error when writing mock response")
}
}))
}
func (m *mockServer) getClient() (repository.Client, func()) {
httpServer := m.getHTTPServer()
config := DemoClientConfiguration
config.URL = httpServer.URL
client, err := NewClient(config)
require.NoError(m.t, err)
// return tearDown method with which will close the test server after the test
tearDown := func() {
httpServer.Close()
}
return client, tearDown
}