Skip to content

Commit 0a8c839

Browse files
committed
Add tests for webapp package
1 parent 90903b2 commit 0a8c839

File tree

2 files changed

+248
-0
lines changed

2 files changed

+248
-0
lines changed

webapp/local_server_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package webapp
2+
3+
import (
4+
"bytes"
5+
"errors"
6+
"net"
7+
"net/http"
8+
"testing"
9+
)
10+
11+
type fakeListener struct {
12+
closed bool
13+
addr *net.TCPAddr
14+
}
15+
16+
func (l *fakeListener) Accept() (net.Conn, error) {
17+
return nil, errors.New("not implemented")
18+
}
19+
func (l *fakeListener) Close() error {
20+
l.closed = true
21+
return nil
22+
}
23+
func (l *fakeListener) Addr() net.Addr {
24+
return l.addr
25+
}
26+
27+
type responseWriter struct {
28+
header http.Header
29+
written bytes.Buffer
30+
status int
31+
}
32+
33+
func (w *responseWriter) Header() http.Header {
34+
if w.header == nil {
35+
w.header = make(http.Header)
36+
}
37+
return w.header
38+
}
39+
func (w *responseWriter) Write(b []byte) (int, error) {
40+
if w.status == 0 {
41+
w.status = 200
42+
}
43+
return w.written.Write(b)
44+
}
45+
func (w *responseWriter) WriteHeader(s int) {
46+
w.status = s
47+
}
48+
49+
func Test_localServer_ServeHTTP(t *testing.T) {
50+
listener := &fakeListener{}
51+
s := &localServer{
52+
CallbackPath: "/hello",
53+
resultChan: make(chan CodeResponse, 1),
54+
listener: listener,
55+
}
56+
57+
w1 := &responseWriter{}
58+
w2 := &responseWriter{}
59+
60+
serveChan := make(chan struct{})
61+
go func() {
62+
req1, _ := http.NewRequest("GET", "http://127.0.0.1:12345/favicon.ico", nil)
63+
s.ServeHTTP(w1, req1)
64+
req2, _ := http.NewRequest("GET", "http://127.0.0.1:12345/hello?code=ABC-123&state=xy%2Fz", nil)
65+
s.ServeHTTP(w2, req2)
66+
serveChan <- struct{}{}
67+
}()
68+
69+
res := <-s.resultChan
70+
if res.Code != "ABC-123" {
71+
t.Errorf("got code %q", res.Code)
72+
}
73+
if res.State != "xy/z" {
74+
t.Errorf("got state %q", res.State)
75+
}
76+
77+
<-serveChan
78+
if w1.status != 404 {
79+
t.Errorf("status = %d", w2.status)
80+
}
81+
82+
if w2.status != 200 {
83+
t.Errorf("status = %d", w2.status)
84+
}
85+
if w2.written.String() != "<p>You may now close this page and return to the client app.</p>" {
86+
t.Errorf("written: %q", w2.written.String())
87+
}
88+
if w2.Header().Get("Content-Type") != "text/html" {
89+
t.Errorf("Content-Type: %v", w2.Header().Get("Content-Type"))
90+
}
91+
if !listener.closed {
92+
t.Error("expected listener to be closed")
93+
}
94+
}

webapp/webapp_flow_test.go

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package webapp
2+
3+
import (
4+
"bytes"
5+
"io/ioutil"
6+
"net"
7+
"net/http"
8+
"net/url"
9+
"testing"
10+
)
11+
12+
func TestFlow_BrowserURL(t *testing.T) {
13+
server := &localServer{
14+
listener: &fakeListener{
15+
addr: &net.TCPAddr{Port: 12345},
16+
},
17+
}
18+
19+
type fields struct {
20+
server *localServer
21+
clientID string
22+
state string
23+
}
24+
type args struct {
25+
baseURL string
26+
params BrowserParams
27+
}
28+
tests := []struct {
29+
name string
30+
fields fields
31+
args args
32+
want string
33+
wantErr bool
34+
}{
35+
{
36+
name: "happy path",
37+
fields: fields{
38+
server: server,
39+
state: "xy/z",
40+
},
41+
args: args{
42+
baseURL: "https://github.com/authorize",
43+
params: BrowserParams{
44+
ClientID: "CLIENT-ID",
45+
RedirectURI: "http://127.0.0.1/hello",
46+
Scopes: []string{"repo", "read:org"},
47+
AllowSignup: true,
48+
},
49+
},
50+
want: "https://github.com/authorize?client_id=CLIENT-ID&redirect_uri=http%3A%2F%2F127.0.0.1%3A12345%2Fhello&scope=repo+read%3Aorg&state=xy%2Fz",
51+
wantErr: false,
52+
},
53+
}
54+
for _, tt := range tests {
55+
t.Run(tt.name, func(t *testing.T) {
56+
flow := &Flow{
57+
server: tt.fields.server,
58+
clientID: tt.fields.clientID,
59+
state: tt.fields.state,
60+
}
61+
got, err := flow.BrowserURL(tt.args.baseURL, tt.args.params)
62+
if (err != nil) != tt.wantErr {
63+
t.Errorf("Flow.BrowserURL() error = %v, wantErr %v", err, tt.wantErr)
64+
return
65+
}
66+
if got != tt.want {
67+
t.Errorf("Flow.BrowserURL() = %v, want %v", got, tt.want)
68+
}
69+
})
70+
}
71+
}
72+
73+
type apiStub struct {
74+
status int
75+
body string
76+
contentType string
77+
}
78+
79+
type postArgs struct {
80+
url string
81+
params url.Values
82+
}
83+
84+
type apiClient struct {
85+
stubs []apiStub
86+
calls []postArgs
87+
88+
postCount int
89+
}
90+
91+
func (c *apiClient) PostForm(u string, params url.Values) (*http.Response, error) {
92+
stub := c.stubs[c.postCount]
93+
c.calls = append(c.calls, postArgs{url: u, params: params})
94+
c.postCount++
95+
return &http.Response{
96+
Body: ioutil.NopCloser(bytes.NewBufferString(stub.body)),
97+
Header: http.Header{
98+
"Content-Type": {stub.contentType},
99+
},
100+
StatusCode: stub.status,
101+
}, nil
102+
}
103+
104+
func TestFlow_AccessToken(t *testing.T) {
105+
server := &localServer{
106+
listener: &fakeListener{
107+
addr: &net.TCPAddr{Port: 12345},
108+
},
109+
resultChan: make(chan CodeResponse),
110+
}
111+
112+
flow := Flow{
113+
server: server,
114+
clientID: "CLIENT-ID",
115+
state: "xy/z",
116+
}
117+
118+
client := &apiClient{
119+
stubs: []apiStub{
120+
{
121+
body: "access_token=ATOKEN&token_type=bearer&scope=repo+gist",
122+
status: 200,
123+
contentType: "application/x-www-form-urlencoded; charset=utf-8",
124+
},
125+
},
126+
}
127+
128+
go func() {
129+
server.resultChan <- CodeResponse{
130+
Code: "ABC-123",
131+
State: "xy/z",
132+
}
133+
}()
134+
135+
token, err := flow.AccessToken(client, "https://github.com/access_token", "OAUTH-SEKRIT")
136+
if err != nil {
137+
t.Fatalf("AccessToken() error: %v", err)
138+
}
139+
140+
if len(client.calls) != 1 {
141+
t.Fatalf("expected 1 HTTP POST, got %d", len(client.calls))
142+
}
143+
apiPost := client.calls[0]
144+
if apiPost.url != "https://github.com/access_token" {
145+
t.Errorf("HTTP POST to %q", apiPost.url)
146+
}
147+
if params := apiPost.params.Encode(); params != "client_id=CLIENT-ID&client_secret=OAUTH-SEKRIT&code=ABC-123&state=xy%2Fz" {
148+
t.Errorf("HTTP POST params: %v", params)
149+
}
150+
151+
if token.Token != "ATOKEN" {
152+
t.Errorf("Token = %q", token.Token)
153+
}
154+
}

0 commit comments

Comments
 (0)