-
Notifications
You must be signed in to change notification settings - Fork 10
/
publicapi_test.go
148 lines (133 loc) · 4.03 KB
/
publicapi_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
package tailscalesd
import (
"context"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
)
func apiBaseForTest(tb testing.TB, surl string) string {
tb.Helper()
u, err := url.Parse(surl)
if err != nil {
tb.Fatal(err)
}
return u.Host
}
func TestPublicAPIDiscovererDevices(t *testing.T) {
var wantPath = "/api/v2/tailnet/testTailnet/devices"
for tn, tc := range map[string]struct {
responder func(w http.ResponseWriter)
wantErr error
want []Device
}{
"returns failed request error when the server responds unsuccessfully": {
responder: func(w http.ResponseWriter) {
w.WriteHeader(http.StatusInternalServerError)
},
wantErr: errFailedAPIRequest,
},
"returns failed request error when the server responds with bad payload": {
responder: func(w http.ResponseWriter) {
w.Header().Set("Content-Type", "text/plain")
fmt.Fprintln(w, "This is decidedly not JSON.")
},
wantErr: errFailedAPIRequest,
},
"returns devices when the server responds with valid JSON": {
responder: func(w http.ResponseWriter) {
w.Header().Set("Content-Type", "application/json; encoding=utf-8")
_, _ = w.Write([]byte(`{"devices": [{"hostname":"testhostname","os":"beos"}]}`))
},
want: []Device{
{
Hostname: "testhostname",
OS: "beos",
Tailnet: "testTailnet",
},
},
},
} {
t.Run(tn, func(t *testing.T) {
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if got, want := r.URL.Path, wantPath; got != want {
t.Errorf("Devices: request URL path mismatch: got: %q want: %q", got, want)
}
tc.responder(w)
}))
defer server.Close()
d := PublicAPI("testTailnet", "testToken", WithHTTPClient(server.Client()), WithAPIHost(apiBaseForTest(t, server.URL)))
got, err := d.Devices(context.TODO())
if got, want := err, tc.wantErr; !errors.Is(got, want) {
t.Errorf("Devices: error mismatch: got: %q want: %q", got, want)
}
// Ignore the API field, which will be set to the arbitrary test
// server's host:port.
if diff := cmp.Diff(got, tc.want, cmpopts.IgnoreFields(Device{}, "API")); diff != "" {
t.Errorf("PublicAPI: mismatch (-got, +want):\n%v", diff)
}
})
}
}
func TestWithAPIHost(t *testing.T) {
want := "test.example.com"
var got publicAPIDiscoverer
WithAPIHost(want)(&got)
if got.apiBase != want {
t.Errorf("WithAPIHost: apiBase mismatch: got: %q want: %q", got.apiBase, want)
}
}
func TestWithHTTPClient(t *testing.T) {
want := &http.Client{}
var got publicAPIDiscoverer
WithHTTPClient(want)(&got)
if got.client != want {
t.Errorf("WithHTTPClient: client mismatch: got: %+v want: %+v", got.client, want)
}
}
type publicAPIOptTester struct {
called int
}
func (t *publicAPIOptTester) Opt() PublicAPIOption {
return func(_ *publicAPIDiscoverer) {
t.called++
}
}
func publicAPIDiscovererComparer(l, r *publicAPIDiscoverer) bool {
return l.client == r.client &&
l.apiBase == r.apiBase &&
l.tailnet == r.tailnet &&
l.token == r.token
}
func TestPublicAPISetsDefaults(t *testing.T) {
got, ok := PublicAPI("testTailnet", "testToken").(*publicAPIDiscoverer)
if !ok {
t.Fatalf("PublicAPI: type mismatch: the Discoverer returned by PublicAPI() was not a *publicAPIDiscoverer")
}
want := &publicAPIDiscoverer{
client: defaultHTTPClient,
apiBase: PublicAPIHost,
tailnet: "testTailnet",
token: "testToken",
}
if diff := cmp.Diff(got, want, cmp.Comparer(publicAPIDiscovererComparer)); diff != "" {
t.Errorf("PublicAPI: mismatch (-got, +want):\n%v", diff)
}
}
func TestPublicAPIInvokesAllOptionsExactlyOnce(t *testing.T) {
optTesters := make([]publicAPIOptTester, 25)
opts := make([]PublicAPIOption, len(optTesters))
for i := range optTesters {
opts[i] = optTesters[i].Opt()
}
_ = PublicAPI("ignored", "ignored", opts...)
for i := range optTesters {
if got, want := optTesters[i].called, 1; got != want {
t.Errorf("PublicAPI: option call mismatch: got: %d want: %d", got, want)
}
}
}