1//
2// Copyright (c) 2018, Joyent, Inc. All rights reserved.
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at http://mozilla.org/MPL/2.0/.
7//
8
9package network_test
10
11import (
12	"context"
13	"fmt"
14	"io/ioutil"
15	"net/http"
16	"path"
17	"strings"
18	"testing"
19
20	triton "github.com/joyent/triton-go"
21	"github.com/joyent/triton-go/network"
22	"github.com/joyent/triton-go/testutils"
23	"github.com/pkg/errors"
24)
25
26var (
27	fakeNetworkID        = "daeb93a2-532e-4bd4-8788-b6b30f10ac17"
28	getNetworkErrorType  = errors.New("unable to get network")
29	listNetworkErrorType = errors.New("unable to list networks")
30)
31
32// Note that this is specific to Joyent Public Cloud and will not pass on
33// private installations of Triton.
34func TestAccNetworks_List(t *testing.T) {
35	testutils.AccTest(t, testutils.TestCase{
36		Steps: []testutils.Step{
37
38			&testutils.StepClient{
39				StateBagKey: "datacenter",
40				CallFunc: func(config *triton.ClientConfig) (interface{}, error) {
41					return network.NewClient(config)
42				},
43			},
44
45			&testutils.StepAPICall{
46				StateBagKey: "networks",
47				CallFunc: func(client interface{}) (interface{}, error) {
48					ctx := context.Background()
49					input := &network.ListInput{}
50					if c, ok := client.(*network.NetworkClient); ok {
51						return c.List(ctx, input)
52					}
53					return nil, fmt.Errorf("Bad client initialization")
54				},
55			},
56
57			&testutils.StepAssertFunc{
58				AssertFunc: func(state testutils.TritonStateBag) error {
59					dcs, ok := state.GetOk("networks")
60					if !ok {
61						return fmt.Errorf("State key %q not found", "networks")
62					}
63
64					toFind := []string{"Joyent-SDC-Private", "Joyent-SDC-Public"}
65					for _, dcName := range toFind {
66						found := false
67						for _, dc := range dcs.([]*network.Network) {
68							if dc.Name == dcName {
69								found = true
70								if dc.Id == "" {
71									return fmt.Errorf("%q has no ID", dc.Name)
72								}
73							}
74						}
75						if !found {
76							return fmt.Errorf("Did not find Network %q", dcName)
77						}
78					}
79
80					return nil
81				},
82			},
83		},
84	})
85}
86
87func TestListNetworks(t *testing.T) {
88	networkClient := MockNetworkClient()
89
90	do := func(ctx context.Context, nc *network.NetworkClient) ([]*network.Network, error) {
91		defer testutils.DeactivateClient()
92
93		networks, err := nc.List(ctx, &network.ListInput{})
94		if err != nil {
95			return nil, err
96		}
97		return networks, nil
98	}
99
100	t.Run("successful", func(t *testing.T) {
101		testutils.RegisterResponder("GET", path.Join("/", accountURL, "networks"), listNetworksSuccess)
102
103		resp, err := do(context.Background(), networkClient)
104		if err != nil {
105			t.Fatal(err)
106		}
107
108		if resp == nil {
109			t.Fatalf("Expected an output but got nil")
110		}
111	})
112
113	t.Run("eof", func(t *testing.T) {
114		testutils.RegisterResponder("GET", path.Join("/", accountURL, "networks"), listNetworksEmpty)
115
116		_, err := do(context.Background(), networkClient)
117		if err == nil {
118			t.Fatal(err)
119		}
120
121		if !strings.Contains(err.Error(), "EOF") {
122			t.Errorf("expected error to contain EOF: found %v", err)
123		}
124	})
125
126	t.Run("bad_decode", func(t *testing.T) {
127		testutils.RegisterResponder("GET", path.Join("/", accountURL, "networks"), listNetworksBadeDecode)
128
129		_, err := do(context.Background(), networkClient)
130		if err == nil {
131			t.Fatal(err)
132		}
133
134		if !strings.Contains(err.Error(), "invalid character") {
135			t.Errorf("expected decode to fail: found %v", err)
136		}
137	})
138
139	t.Run("error", func(t *testing.T) {
140		testutils.RegisterResponder("GET", path.Join("/", accountURL, "networks"), listNetworksError)
141
142		resp, err := do(context.Background(), networkClient)
143		if err == nil {
144			t.Fatal(err)
145		}
146		if resp != nil {
147			t.Error("expected resp to be nil")
148		}
149
150		if !strings.Contains(err.Error(), "unable to list networks") {
151			t.Errorf("expected error to equal testError: found %v", err)
152		}
153	})
154}
155
156func TestGetNetwork(t *testing.T) {
157	networkClient := MockNetworkClient()
158
159	do := func(ctx context.Context, nc *network.NetworkClient) (*network.Network, error) {
160		defer testutils.DeactivateClient()
161
162		network, err := nc.Get(ctx, &network.GetInput{
163			ID: fakeNetworkID,
164		})
165		if err != nil {
166			return nil, err
167		}
168		return network, nil
169	}
170
171	t.Run("successful", func(t *testing.T) {
172		testutils.RegisterResponder("GET", path.Join("/", accountURL, "networks", fakeNetworkID), getNetworkSuccess)
173
174		resp, err := do(context.Background(), networkClient)
175		if err != nil {
176			t.Fatal(err)
177		}
178
179		if resp == nil {
180			t.Fatalf("Expected an output but got nil")
181		}
182	})
183
184	t.Run("eof", func(t *testing.T) {
185		testutils.RegisterResponder("GET", path.Join("/", accountURL, "networks", fakeNetworkID), getNetworkEmpty)
186
187		_, err := do(context.Background(), networkClient)
188		if err == nil {
189			t.Fatal(err)
190		}
191
192		if !strings.Contains(err.Error(), "EOF") {
193			t.Errorf("expected error to contain EOF: found %v", err)
194		}
195	})
196
197	t.Run("bad_decode", func(t *testing.T) {
198		testutils.RegisterResponder("GET", path.Join("/", accountURL, "networks", fakeNetworkID), getNetworkBadeDecode)
199
200		_, err := do(context.Background(), networkClient)
201		if err == nil {
202			t.Fatal(err)
203		}
204
205		if !strings.Contains(err.Error(), "invalid character") {
206			t.Errorf("expected decode to fail: found %v", err)
207		}
208	})
209
210	t.Run("error", func(t *testing.T) {
211		testutils.RegisterResponder("GET", path.Join("/", accountURL, "networks"), getNetworkError)
212
213		resp, err := do(context.Background(), networkClient)
214		if err == nil {
215			t.Fatal(err)
216		}
217		if resp != nil {
218			t.Error("expected resp to be nil")
219		}
220
221		if !strings.Contains(err.Error(), "unable to get network") {
222			t.Errorf("expected error to equal testError: found %v", err)
223		}
224	})
225}
226
227func getNetworkSuccess(req *http.Request) (*http.Response, error) {
228	header := http.Header{}
229	header.Add("Content-Type", "application/json")
230
231	body := strings.NewReader(`{
232  "id": "daeb93a2-532e-4bd4-8788-b6b30f10ac17",
233  "name": "external",
234  "public": true
235}
236`)
237	return &http.Response{
238		StatusCode: http.StatusOK,
239		Header:     header,
240		Body:       ioutil.NopCloser(body),
241	}, nil
242}
243
244func getNetworkError(req *http.Request) (*http.Response, error) {
245	return nil, getNetworkErrorType
246}
247
248func getNetworkBadeDecode(req *http.Request) (*http.Response, error) {
249	header := http.Header{}
250	header.Add("Content-Type", "application/json")
251
252	body := strings.NewReader(`{
253  "id": "daeb93a2-532e-4bd4-8788-b6b30f10ac17",
254  "name": "external",
255  "public": true,
256}`)
257	return &http.Response{
258		StatusCode: http.StatusOK,
259		Header:     header,
260		Body:       ioutil.NopCloser(body),
261	}, nil
262}
263
264func getNetworkEmpty(req *http.Request) (*http.Response, error) {
265	header := http.Header{}
266	header.Add("Content-Type", "application/json")
267	return &http.Response{
268		StatusCode: http.StatusOK,
269		Header:     header,
270		Body:       ioutil.NopCloser(strings.NewReader("")),
271	}, nil
272}
273
274func listNetworksEmpty(req *http.Request) (*http.Response, error) {
275	header := http.Header{}
276	header.Add("Content-Type", "application/json")
277	return &http.Response{
278		StatusCode: http.StatusOK,
279		Header:     header,
280		Body:       ioutil.NopCloser(strings.NewReader("")),
281	}, nil
282}
283
284func listNetworksSuccess(req *http.Request) (*http.Response, error) {
285	header := http.Header{}
286	header.Add("Content-Type", "application/json")
287
288	body := strings.NewReader(`[
289	{
290    "id": "daeb93a2-532e-4bd4-8788-b6b30f10ac17",
291    "name": "external",
292    "public": true
293  }
294]`)
295	return &http.Response{
296		StatusCode: http.StatusOK,
297		Header:     header,
298		Body:       ioutil.NopCloser(body),
299	}, nil
300}
301
302func listNetworksBadeDecode(req *http.Request) (*http.Response, error) {
303	header := http.Header{}
304	header.Add("Content-Type", "application/json")
305
306	body := strings.NewReader(`{[
307	{
308    "id": "daeb93a2-532e-4bd4-8788-b6b30f10ac17",
309    "name": "external",
310    "public": true
311  }
312]}`)
313	return &http.Response{
314		StatusCode: http.StatusOK,
315		Header:     header,
316		Body:       ioutil.NopCloser(body),
317	}, nil
318}
319
320func listNetworksError(req *http.Request) (*http.Response, error) {
321	return nil, listNetworkErrorType
322}
323