1package haip
2
3import (
4	"github.com/stretchr/testify/assert"
5	"github.com/stretchr/testify/require"
6	"github.com/transip/gotransip/v6"
7	"github.com/transip/gotransip/v6/repository"
8	"io/ioutil"
9	"net"
10	"net/http"
11	"net/http/httptest"
12	"testing"
13)
14
15// mockServer struct is used to test the how the client sends a request
16// and responds to a servers response
17type mockServer struct {
18	t               *testing.T
19	expectedURL     string
20	expectedMethod  string
21	statusCode      int
22	expectedRequest string
23	response        string
24	skipRequestBody bool
25}
26
27func (m *mockServer) getHTTPServer() *httptest.Server {
28	return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
29		assert.Equal(m.t, m.expectedURL, req.URL.String()) // check if right expectedURL is called
30
31		if m.skipRequestBody == false && req.ContentLength != 0 {
32			// get the request body
33			// and check if the body matches the expected request body
34			body, err := ioutil.ReadAll(req.Body)
35			require.NoError(m.t, err)
36			assert.Equal(m.t, m.expectedRequest, string(body))
37		}
38
39		assert.Equal(m.t, m.expectedMethod, req.Method) // check if the right expectedRequest expectedMethod is used
40		rw.WriteHeader(m.statusCode)                    // respond with given status code
41
42		if m.response != "" {
43			_, err := rw.Write([]byte(m.response))
44			require.NoError(m.t, err, "error when writing mock response")
45		}
46	}))
47}
48
49func (m *mockServer) getClient() (*repository.Client, func()) {
50	httpServer := m.getHTTPServer()
51	config := gotransip.DemoClientConfiguration
52	config.URL = httpServer.URL
53	client, err := gotransip.NewClient(config)
54	require.NoError(m.t, err)
55
56	// return tearDown method with which will close the test server after the test
57	tearDown := func() {
58		httpServer.Close()
59	}
60
61	return &client, tearDown
62}
63
64func TestRepository_GetAll(t *testing.T) {
65	const apiResponse = `{ "haips": [ { "name": "example-haip", "description": "frontend cluster", "status": "active", "isLoadBalancingEnabled": true, "loadBalancingMode": "cookie", "stickyCookieName": "PHPSESSID", "healthCheckInterval": 3000, "httpHealthCheckPath": "/status.php", "httpHealthCheckPort": 443, "httpHealthCheckSsl": true, "ipv4Address": "37.97.254.7", "ipv6Address": "2a01:7c8:3:1337::1", "ipSetup": "ipv6to4", "ptrRecord": "frontend.example.com", "ipAddresses": [ "10.3.37.1", "10.3.38.1" ], "tlsMode": "tls12", "isLocked": false } ] } `
66	server := mockServer{t: t, expectedURL: "/haips", expectedMethod: "GET", statusCode: 200, response: apiResponse}
67	client, tearDown := server.getClient()
68	defer tearDown()
69	repo := Repository{Client: *client}
70
71	all, err := repo.GetAll()
72	require.NoError(t, err)
73	require.Equal(t, 1, len(all))
74
75	assert.Equal(t, "example-haip", all[0].Name)
76	assert.Equal(t, "frontend cluster", all[0].Description)
77	assert.EqualValues(t, "active", all[0].Status)
78	assert.Equal(t, true, all[0].IsLoadBalancingEnabled)
79	assert.EqualValues(t, "cookie", all[0].LoadBalancingMode)
80	assert.Equal(t, "PHPSESSID", all[0].StickyCookieName)
81	assert.EqualValues(t, 3000, all[0].HealthCheckInterval)
82	assert.Equal(t, "/status.php", all[0].HTTPHealthCheckPath)
83	assert.Equal(t, 443, all[0].HTTPHealthCheckPort)
84	assert.Equal(t, true, all[0].HTTPHealthCheckSsl)
85	assert.Equal(t, "37.97.254.7", all[0].IPv4Address.String())
86	assert.Equal(t, "2a01:7c8:3:1337::1", all[0].IPv6Address.String())
87	assert.EqualValues(t, "ipv6to4", all[0].IPSetup)
88	assert.Equal(t, "frontend.example.com", all[0].PtrRecord)
89	require.Equal(t, 2, len(all[0].IPAddresses))
90	assert.Equal(t, "10.3.37.1", all[0].IPAddresses[0].String())
91	assert.Equal(t, "10.3.38.1", all[0].IPAddresses[1].String())
92	assert.EqualValues(t, "tls12", all[0].TLSMode)
93	assert.Equal(t, false, all[0].IsLocked)
94}
95
96func TestRepository_GetSelection(t *testing.T) {
97	const apiResponse = `{ "haips": [ { "name": "example-haip", "description": "frontend cluster", "status": "active", "isLoadBalancingEnabled": true, "loadBalancingMode": "cookie", "stickyCookieName": "PHPSESSID", "healthCheckInterval": 3000, "httpHealthCheckPath": "/status.php", "httpHealthCheckPort": 443, "httpHealthCheckSsl": true, "ipv4Address": "37.97.254.7", "ipv6Address": "2a01:7c8:3:1337::1", "ipSetup": "ipv4to6", "ptrRecord": "frontend.example.com", "ipAddresses": [ "10.3.37.1", "10.3.38.1" ], "tlsMode": "tls12", "isLocked": true } ] } `
98	server := mockServer{t: t, expectedURL: "/haips?page=1&pageSize=25", expectedMethod: "GET", statusCode: 200, response: apiResponse}
99	client, tearDown := server.getClient()
100	defer tearDown()
101	repo := Repository{Client: *client}
102
103	all, err := repo.GetSelection(1, 25)
104	require.NoError(t, err)
105	require.Equal(t, 1, len(all))
106
107	assert.Equal(t, "example-haip", all[0].Name)
108	assert.Equal(t, "frontend cluster", all[0].Description)
109	assert.EqualValues(t, "active", all[0].Status)
110	assert.Equal(t, true, all[0].IsLoadBalancingEnabled)
111	assert.EqualValues(t, "cookie", all[0].LoadBalancingMode)
112	assert.Equal(t, "PHPSESSID", all[0].StickyCookieName)
113	assert.EqualValues(t, 3000, all[0].HealthCheckInterval)
114	assert.Equal(t, "/status.php", all[0].HTTPHealthCheckPath)
115	assert.Equal(t, 443, all[0].HTTPHealthCheckPort)
116	assert.Equal(t, true, all[0].HTTPHealthCheckSsl)
117	assert.Equal(t, "37.97.254.7", all[0].IPv4Address.String())
118	assert.Equal(t, "2a01:7c8:3:1337::1", all[0].IPv6Address.String())
119	assert.EqualValues(t, "ipv4to6", all[0].IPSetup)
120	assert.Equal(t, "frontend.example.com", all[0].PtrRecord)
121	require.Equal(t, 2, len(all[0].IPAddresses))
122	assert.Equal(t, "10.3.37.1", all[0].IPAddresses[0].String())
123	assert.Equal(t, "10.3.38.1", all[0].IPAddresses[1].String())
124	assert.EqualValues(t, "tls12", all[0].TLSMode)
125	assert.Equal(t, true, all[0].IsLocked)
126}
127
128func TestRepository_GetByName(t *testing.T) {
129	const apiResponse = `{ "haip": { "name": "example-haip", "description": "frontend cluster", "status": "active", "isLoadBalancingEnabled": true, "loadBalancingMode": "cookie", "stickyCookieName": "PHPSESSID", "healthCheckInterval": 3000, "httpHealthCheckPath": "/status.php", "httpHealthCheckPort": 443, "httpHealthCheckSsl": true, "ipv4Address": "37.97.254.7", "ipv6Address": "2a01:7c8:3:1337::1", "ipSetup": "ipv6to4", "ptrRecord": "frontend.example.com", "ipAddresses": [ "10.3.37.1", "10.3.38.1" ], "tlsMode": "tls12", "isLocked": false } }`
130	server := mockServer{t: t, expectedURL: "/haips/example-haip", expectedMethod: "GET", statusCode: 200, response: apiResponse}
131	client, tearDown := server.getClient()
132	defer tearDown()
133	repo := Repository{Client: *client}
134
135	haip, err := repo.GetByName("example-haip")
136	require.NoError(t, err)
137
138	assert.Equal(t, "example-haip", haip.Name)
139	assert.Equal(t, "frontend cluster", haip.Description)
140	assert.EqualValues(t, "active", haip.Status)
141	assert.Equal(t, true, haip.IsLoadBalancingEnabled)
142	assert.EqualValues(t, "cookie", haip.LoadBalancingMode)
143	assert.Equal(t, "PHPSESSID", haip.StickyCookieName)
144	assert.EqualValues(t, 3000, haip.HealthCheckInterval)
145	assert.Equal(t, "/status.php", haip.HTTPHealthCheckPath)
146	assert.Equal(t, 443, haip.HTTPHealthCheckPort)
147	assert.Equal(t, true, haip.HTTPHealthCheckSsl)
148	assert.Equal(t, "37.97.254.7", haip.IPv4Address.String())
149	assert.Equal(t, "2a01:7c8:3:1337::1", haip.IPv6Address.String())
150	assert.EqualValues(t, "ipv6to4", haip.IPSetup)
151	assert.Equal(t, "frontend.example.com", haip.PtrRecord)
152	require.Equal(t, 2, len(haip.IPAddresses))
153	assert.Equal(t, "10.3.37.1", haip.IPAddresses[0].String())
154	assert.Equal(t, "10.3.38.1", haip.IPAddresses[1].String())
155	assert.EqualValues(t, "tls12", haip.TLSMode)
156	assert.Equal(t, false, haip.IsLocked)
157}
158
159func TestRepository_Order(t *testing.T) {
160	const expectedRequestBody = `{"productName":"haip-pro-contract","description":"myhaip01"}`
161	server := mockServer{t: t, expectedURL: "/haips", expectedMethod: "POST", statusCode: 201, expectedRequest: expectedRequestBody}
162	client, tearDown := server.getClient()
163	defer tearDown()
164	repo := Repository{Client: *client}
165
166	err := repo.Order("haip-pro-contract", "myhaip01")
167	require.NoError(t, err)
168}
169
170func TestRepository_Update(t *testing.T) {
171	const expectedRequestBody = `{"haip":{"name":"example-haip","description":"frontend cluster","status":"active","isLoadBalancingEnabled":true,"loadBalancingMode":"cookie","stickyCookieName":"PHPSESSID","healthCheckInterval":3000,"httpHealthCheckPath":"/status.php","httpHealthCheckPort":443,"httpHealthCheckSsl":true,"ipv4Address":"37.97.254.7","ipv6Address":"2a01:7c8:3:1337::1","ipSetup":"ipv6to4","ptrRecord":"frontend.example.com","ipAddresses":["10.3.37.1","10.3.38.1"],"tlsMode":"tls10_11_12"}}`
172	server := mockServer{t: t, expectedURL: "/haips/example-haip", expectedMethod: "PUT", statusCode: 204, expectedRequest: expectedRequestBody}
173	client, tearDown := server.getClient()
174	defer tearDown()
175	repo := Repository{Client: *client}
176
177	haip := Haip{
178		Name:                   "example-haip",
179		Description:            "frontend cluster",
180		Status:                 "active",
181		IsLoadBalancingEnabled: true,
182		LoadBalancingMode:      "cookie",
183		StickyCookieName:       "PHPSESSID",
184		HealthCheckInterval:    3000,
185		HTTPHealthCheckPath:    "/status.php",
186		HTTPHealthCheckPort:    443,
187		HTTPHealthCheckSsl:     true,
188		IPv4Address:            net.ParseIP("37.97.254.7"),
189		IPv6Address:            net.ParseIP("2a01:7c8:3:1337::1"),
190		IPSetup:                "ipv6to4",
191		PtrRecord:              "frontend.example.com",
192		IPAddresses:            []net.IP{net.ParseIP("10.3.37.1"), net.ParseIP("10.3.38.1")},
193		TLSMode:                TLSModeMinTLS10,
194	}
195
196	err := repo.Update(haip)
197	require.NoError(t, err)
198}
199
200func TestRepository_Cancel(t *testing.T) {
201	const expectedRequestBody = `{"endTime":"immediately"}`
202	server := mockServer{t: t, expectedURL: "/haips/example-haip", expectedMethod: "DELETE", statusCode: 204, expectedRequest: expectedRequestBody}
203	client, tearDown := server.getClient()
204	defer tearDown()
205	repo := Repository{Client: *client}
206
207	err := repo.Cancel("example-haip", gotransip.CancellationTimeImmediately)
208	require.NoError(t, err)
209}
210
211func TestRepository_GetAllCertificates(t *testing.T) {
212	const apiResponse = `{ "certificates": [ { "id": 25478, "commonName": "example.com", "expirationDate": "2019-11-23" } ] }`
213	server := mockServer{t: t, expectedURL: "/haips/example-haip/certificates", expectedMethod: "GET", statusCode: 200, response: apiResponse}
214	client, tearDown := server.getClient()
215	defer tearDown()
216	repo := Repository{Client: *client}
217
218	all, err := repo.GetAllCertificates("example-haip")
219	require.NoError(t, err)
220	require.Equal(t, 1, len(all))
221
222	assert.EqualValues(t, 25478, all[0].ID)
223	assert.Equal(t, "example.com", all[0].CommonName)
224	assert.Equal(t, "2019-11-23", all[0].ExpirationDate)
225}
226
227func TestRepository_AddCertificate(t *testing.T) {
228	const expectedRequestBody = `{"sslCertificateId":1337}`
229	server := mockServer{t: t, expectedURL: "/haips/example-haip/certificates", expectedMethod: "POST", statusCode: 201, expectedRequest: expectedRequestBody}
230	client, tearDown := server.getClient()
231	defer tearDown()
232	repo := Repository{Client: *client}
233
234	err := repo.AddCertificate("example-haip", 1337)
235	require.NoError(t, err)
236}
237
238func TestRepository_AddLetsEncryptCertificate(t *testing.T) {
239	const expectedRequestBody = `{"commonName":"foobar.example.com"}`
240	server := mockServer{t: t, expectedURL: "/haips/example-haip/certificates", expectedMethod: "POST", statusCode: 201, expectedRequest: expectedRequestBody}
241	client, tearDown := server.getClient()
242	defer tearDown()
243	repo := Repository{Client: *client}
244
245	err := repo.AddLetsEncryptCertificate("example-haip", "foobar.example.com")
246	require.NoError(t, err)
247}
248
249func TestRepository_DetachCertificate(t *testing.T) {
250	server := mockServer{t: t, expectedURL: "/haips/example-haip/certificates/1337", expectedMethod: "DELETE", statusCode: 204}
251	client, tearDown := server.getClient()
252	defer tearDown()
253	repo := Repository{Client: *client}
254
255	err := repo.DetachCertificate("example-haip", 1337)
256	require.NoError(t, err)
257}
258
259func TestRepository_GetAttachedIPAddresses(t *testing.T) {
260	const apiResponse = `{ "ipAddresses": [ "149.13.3.7", "149.31.33.7" ] }`
261	server := mockServer{t: t, expectedURL: "/haips/example-haip/ip-addresses", expectedMethod: "GET", statusCode: 200, response: apiResponse}
262	client, tearDown := server.getClient()
263	defer tearDown()
264	repo := Repository{Client: *client}
265
266	all, err := repo.GetAttachedIPAddresses("example-haip")
267	require.NoError(t, err)
268	require.Equal(t, 2, len(all))
269
270	assert.Equal(t, "149.13.3.7", all[0].String())
271	assert.Equal(t, "149.31.33.7", all[1].String())
272}
273
274func TestRepository_SetAttachedIPAddresses(t *testing.T) {
275	const expectedRequestBody = `{"ipAddresses":["10.3.37.1","10.3.37.2"]}`
276	server := mockServer{t: t, expectedURL: "/haips/example-haip/ip-addresses", expectedMethod: "PUT", statusCode: 204, expectedRequest: expectedRequestBody}
277	client, tearDown := server.getClient()
278	defer tearDown()
279	repo := Repository{Client: *client}
280
281	ips := []net.IP{net.ParseIP("10.3.37.1"), net.ParseIP("10.3.37.2")}
282
283	err := repo.SetAttachedIPAddresses("example-haip", ips)
284	require.NoError(t, err)
285}
286
287func TestRepository_DetachIPAddresses(t *testing.T) {
288	server := mockServer{t: t, expectedURL: "/haips/example-haip/ip-addresses", expectedMethod: "DELETE", statusCode: 204}
289	client, tearDown := server.getClient()
290	defer tearDown()
291	repo := Repository{Client: *client}
292
293	err := repo.DetachIPAddresses("example-haip")
294	require.NoError(t, err)
295}
296
297func TestRepository_GetPortConfigurations(t *testing.T) {
298	const apiResponse = `{ "portConfigurations": [ { "id": 9865, "name": "Website Traffic", "sourcePort": 80, "targetPort": 80, "mode": "http", "endpointSslMode": "off" } ] } `
299	server := mockServer{t: t, expectedURL: "/haips/example-haip/port-configurations", expectedMethod: "GET", statusCode: 200, response: apiResponse}
300	client, tearDown := server.getClient()
301	defer tearDown()
302	repo := Repository{Client: *client}
303
304	all, err := repo.GetPortConfigurations("example-haip")
305	require.NoError(t, err)
306	require.Equal(t, 1, len(all))
307
308	assert.EqualValues(t, 9865, all[0].ID)
309	assert.Equal(t, "Website Traffic", all[0].Name)
310	assert.Equal(t, 80, all[0].SourcePort)
311	assert.Equal(t, 80, all[0].TargetPort)
312	assert.EqualValues(t, "http", all[0].Mode)
313	assert.Equal(t, "off", all[0].EndpointSslMode)
314}
315
316func TestRepository_GetPortConfiguration(t *testing.T) {
317	const apiResponse = `{ "portConfiguration": { "id": 9865, "name": "Website Traffic", "sourcePort": 80, "targetPort": 80, "mode": "http", "endpointSslMode": "off" } } `
318	server := mockServer{t: t, expectedURL: "/haips/example-haip/port-configurations/9865", expectedMethod: "GET", statusCode: 200, response: apiResponse}
319	client, tearDown := server.getClient()
320	defer tearDown()
321	repo := Repository{Client: *client}
322
323	configuration, err := repo.GetPortConfiguration("example-haip", 9865)
324	require.NoError(t, err)
325
326	assert.EqualValues(t, 9865, configuration.ID)
327	assert.Equal(t, "Website Traffic", configuration.Name)
328	assert.Equal(t, 80, configuration.SourcePort)
329	assert.Equal(t, 80, configuration.TargetPort)
330	assert.EqualValues(t, "http", configuration.Mode)
331	assert.Equal(t, "off", configuration.EndpointSslMode)
332}
333
334func TestRepository_AddPortConfiguration(t *testing.T) {
335	const expectedRequestBody = `{"name":"Website Traffic","sourcePort":443,"targetPort":443,"mode":"https","endpointSslMode":"on"}`
336	server := mockServer{t: t, expectedURL: "/haips/example-haip/port-configurations", expectedMethod: "POST", statusCode: 201, expectedRequest: expectedRequestBody}
337	client, tearDown := server.getClient()
338	defer tearDown()
339	repo := Repository{Client: *client}
340
341	configuration := PortConfiguration{
342		Name:            "Website Traffic",
343		SourcePort:      443,
344		TargetPort:      443,
345		Mode:            "https",
346		EndpointSslMode: "on",
347	}
348	err := repo.AddPortConfiguration("example-haip", configuration)
349	require.NoError(t, err)
350}
351
352func TestRepository_UpdatePortConfiguration(t *testing.T) {
353	const expectedRequestBody = `{"portConfiguration":{"id":9865,"name":"Website Traffic","sourcePort":443,"targetPort":443,"mode":"https","endpointSslMode":"on"}}`
354	server := mockServer{t: t, expectedURL: "/haips/example-haip/port-configurations/9865", expectedMethod: "PUT", statusCode: 204, expectedRequest: expectedRequestBody}
355	client, tearDown := server.getClient()
356	defer tearDown()
357	repo := Repository{Client: *client}
358
359	configuration := PortConfiguration{
360		ID:              9865,
361		Name:            "Website Traffic",
362		SourcePort:      443,
363		TargetPort:      443,
364		Mode:            "https",
365		EndpointSslMode: "on",
366	}
367	err := repo.UpdatePortConfiguration("example-haip", configuration)
368	require.NoError(t, err)
369}
370
371func TestRepository_RemovePortConfiguration(t *testing.T) {
372	server := mockServer{t: t, expectedURL: "/haips/example-haip/port-configurations/1337", expectedMethod: "DELETE", statusCode: 204}
373	client, tearDown := server.getClient()
374	defer tearDown()
375	repo := Repository{Client: *client}
376
377	err := repo.RemovePortConfiguration("example-haip", 1337)
378	require.NoError(t, err)
379}
380
381func TestRepository_GetStatusReport(t *testing.T) {
382	const apiResponse = `{ "statusReport": [ { "ipAddress": "136.10.14.1", "port": 80, "ipVersion": 4, "loadBalancerName": "lb0", "loadBalancerIp": "136.144.151.255", "state": "up", "lastChange": "2019-09-29 16:51:18" } ] }`
383	server := mockServer{t: t, expectedURL: "/haips/example-haip/status-reports", expectedMethod: "GET", statusCode: 200, response: apiResponse}
384	client, tearDown := server.getClient()
385	defer tearDown()
386	repo := Repository{Client: *client}
387
388	all, err := repo.GetStatusReport("example-haip")
389	require.NoError(t, err)
390	require.Equal(t, 1, len(all))
391
392	assert.Equal(t, "136.10.14.1", all[0].IPAddress.String())
393	assert.Equal(t, 80, all[0].Port)
394	assert.Equal(t, 4, all[0].IPVersion)
395	assert.Equal(t, "lb0", all[0].LoadBalancerName)
396	assert.Equal(t, "136.144.151.255", all[0].LoadBalancerIP.String())
397	assert.Equal(t, "up", all[0].State)
398	assert.Equal(t, "2019-09-29 16:51:18", all[0].LastChange.Format("2006-01-02 15:04:05"))
399}
400