Skip to content

Commit e2ddfd5

Browse files
authored
Merge pull request #123 from nhooyr/test
Improve tests
2 parents 695d679 + 8cfcf43 commit e2ddfd5

20 files changed

+1207
-374
lines changed

.circleci/config.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ version: 2
22
jobs:
33
fmt:
44
docker:
5-
- image: nhooyr/websocket-ci
5+
- image: nhooyr/websocket-ci@sha256:371ca985ce2548840aeb0f8434a551708cdfe0628be722c361958e65cdded945
66
steps:
77
- checkout
88
- restore_cache:
@@ -19,7 +19,7 @@ jobs:
1919

2020
lint:
2121
docker:
22-
- image: nhooyr/websocket-ci
22+
- image: nhooyr/websocket-ci@sha256:371ca985ce2548840aeb0f8434a551708cdfe0628be722c361958e65cdded945
2323
steps:
2424
- checkout
2525
- restore_cache:
@@ -36,7 +36,7 @@ jobs:
3636

3737
test:
3838
docker:
39-
- image: nhooyr/websocket-ci
39+
- image: nhooyr/websocket-ci@sha256:371ca985ce2548840aeb0f8434a551708cdfe0628be722c361958e65cdded945
4040
steps:
4141
- checkout
4242
- restore_cache:

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ For a production quality example that shows off the full API, see the [echo exam
3434

3535
```go
3636
http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
37-
c, err := websocket.Accept(w, r, websocket.AcceptOptions{})
37+
c, err := websocket.Accept(w, r, nil)
3838
if err != nil {
3939
// ...
4040
}
@@ -64,7 +64,7 @@ in net/http](https://github.com/golang/go/issues/26937#issuecomment-415855861) t
6464
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
6565
defer cancel()
6666

67-
c, _, err := websocket.Dial(ctx, "ws://localhost:8080", websocket.DialOptions{})
67+
c, _, err := websocket.Dial(ctx, "ws://localhost:8080", nil)
6868
if err != nil {
6969
// ...
7070
}

accept.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,19 @@ func verifyClientRequest(w http.ResponseWriter, r *http.Request) error {
8484
//
8585
// If an error occurs, Accept will always write an appropriate response so you do not
8686
// have to.
87-
func Accept(w http.ResponseWriter, r *http.Request, opts AcceptOptions) (*Conn, error) {
87+
func Accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (*Conn, error) {
8888
c, err := accept(w, r, opts)
8989
if err != nil {
9090
return nil, xerrors.Errorf("failed to accept websocket connection: %w", err)
9191
}
9292
return c, nil
9393
}
9494

95-
func accept(w http.ResponseWriter, r *http.Request, opts AcceptOptions) (*Conn, error) {
95+
func accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (*Conn, error) {
96+
if opts == nil {
97+
opts = &AcceptOptions{}
98+
}
99+
96100
err := verifyClientRequest(w, r)
97101
if err != nil {
98102
return nil, err

accept_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,39 @@ import (
66
"testing"
77
)
88

9+
func TestAccept(t *testing.T) {
10+
t.Parallel()
11+
12+
t.Run("badClientHandshake", func(t *testing.T) {
13+
t.Parallel()
14+
15+
w := httptest.NewRecorder()
16+
r := httptest.NewRequest("GET", "/", nil)
17+
18+
_, err := Accept(w, r, nil)
19+
if err == nil {
20+
t.Fatalf("unexpected error value: %v", err)
21+
}
22+
23+
})
24+
25+
t.Run("requireHttpHijacker", func(t *testing.T) {
26+
t.Parallel()
27+
28+
w := httptest.NewRecorder()
29+
r := httptest.NewRequest("GET", "/", nil)
30+
r.Header.Set("Connection", "Upgrade")
31+
r.Header.Set("Upgrade", "websocket")
32+
r.Header.Set("Sec-WebSocket-Version", "13")
33+
r.Header.Set("Sec-WebSocket-Key", "meow123")
34+
35+
_, err := Accept(w, r, nil)
36+
if err == nil || !strings.Contains(err.Error(), "http.Hijacker") {
37+
t.Fatalf("unexpected error value: %v", err)
38+
}
39+
})
40+
}
41+
942
func Test_verifyClientHandshake(t *testing.T) {
1043
t.Parallel()
1144

ci/image/Dockerfile

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ ENV GOFLAGS="-mod=readonly"
66
ENV PAGER=cat
77

88
RUN apt-get update && \
9-
apt-get install -y shellcheck python-pip npm && \
10-
pip2 install autobahntestsuite && \
9+
apt-get install -y shellcheck npm && \
1110
npm install -g prettier
1211

13-
RUN git config --global color.ui always
12+
RUN git config --global color.ui always

ci/test.sh

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,34 @@ set -euo pipefail
44
cd "$(dirname "${0}")"
55
cd "$(git rev-parse --show-toplevel)"
66

7-
mkdir -p ci/out/websocket
8-
testFlags=(
7+
argv=(
8+
go run gotest.tools/gotestsum
9+
# https://circleci.com/docs/2.0/collect-test-data/
10+
"--junitfile=ci/out/websocket/testReport.xml"
11+
"--format=short-verbose"
12+
--
913
-race
1014
"-vet=off"
1115
"-bench=."
16+
)
17+
# Interactive usage probably does not want to enable benchmarks, race detection
18+
# turn off vet or use gotestsum by default.
19+
if [[ $# -gt 0 ]]; then
20+
argv=(go test "$@")
21+
fi
22+
23+
# We always want coverage.
24+
argv+=(
1225
"-coverprofile=ci/out/coverage.prof"
1326
"-coverpkg=./..."
1427
)
15-
# https://circleci.com/docs/2.0/collect-test-data/
16-
go run gotest.tools/gotestsum \
17-
--junitfile ci/out/websocket/testReport.xml \
18-
--format=short-verbose \
19-
-- "${testFlags[@]}"
28+
29+
mkdir -p ci/out/websocket
30+
"${argv[@]}"
31+
32+
# Removes coverage of generated files.
33+
grep -v _string.go < ci/out/coverage.prof > ci/out/coverage2.prof
34+
mv ci/out/coverage2.prof ci/out/coverage.prof
2035

2136
go tool cover -html=ci/out/coverage.prof -o=ci/out/coverage.html
2237
if [[ ${CI:-} ]]; then

dial.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,23 @@ type DialOptions struct {
4141
// This function requires at least Go 1.12 to succeed as it uses a new feature
4242
// in net/http to perform WebSocket handshakes and get a writable body
4343
// from the transport. See https://github.com/golang/go/issues/26937#issuecomment-415855861
44-
func Dial(ctx context.Context, u string, opts DialOptions) (*Conn, *http.Response, error) {
44+
func Dial(ctx context.Context, u string, opts *DialOptions) (*Conn, *http.Response, error) {
4545
c, r, err := dial(ctx, u, opts)
4646
if err != nil {
4747
return nil, r, xerrors.Errorf("failed to websocket dial: %w", err)
4848
}
4949
return c, r, nil
5050
}
5151

52-
func dial(ctx context.Context, u string, opts DialOptions) (_ *Conn, _ *http.Response, err error) {
52+
func dial(ctx context.Context, u string, opts *DialOptions) (_ *Conn, _ *http.Response, err error) {
53+
if opts == nil {
54+
opts = &DialOptions{}
55+
}
56+
57+
// Shallow copy to ensure defaults do not affect user passed options.
58+
opts2 := *opts
59+
opts = &opts2
60+
5361
if opts.HTTPClient == nil {
5462
opts.HTTPClient = http.DefaultClient
5563
}

dial_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,60 @@
11
package websocket
22

33
import (
4+
"context"
45
"net/http"
56
"net/http/httptest"
67
"testing"
8+
"time"
79
)
810

11+
func TestBadDials(t *testing.T) {
12+
t.Parallel()
13+
14+
testCases := []struct {
15+
name string
16+
url string
17+
opts *DialOptions
18+
}{
19+
{
20+
name: "badURL",
21+
url: "://noscheme",
22+
},
23+
{
24+
name: "badURLScheme",
25+
url: "ftp://nhooyr.io",
26+
},
27+
{
28+
name: "badHTTPClient",
29+
url: "ws://nhooyr.io",
30+
opts: &DialOptions{
31+
HTTPClient: &http.Client{
32+
Timeout: time.Minute,
33+
},
34+
},
35+
},
36+
{
37+
name: "badTLS",
38+
url: "wss://totallyfake.nhooyr.io",
39+
},
40+
}
41+
42+
for _, tc := range testCases {
43+
tc := tc
44+
t.Run(tc.name, func(t *testing.T) {
45+
t.Parallel()
46+
47+
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
48+
defer cancel()
49+
50+
_, _, err := Dial(ctx, tc.url, tc.opts)
51+
if err == nil {
52+
t.Fatalf("expected non nil error: %+v", err)
53+
}
54+
})
55+
}
56+
}
57+
958
func Test_verifyServerHandshake(t *testing.T) {
1059
t.Parallel()
1160

docs/CONTRIBUTING.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,21 @@ browse coverage.
3434

3535
You can run CI locally. The various steps are located in `ci/*.sh`.
3636

37-
1. `ci/fmt.sh` requires node (specifically prettier).
38-
1. `ci/lint.sh` requires [shellcheck](https://github.com/koalaman/shellcheck#installing).
39-
1. `ci/test.sh` requires the [Autobahn Test suite pip package](https://github.com/crossbario/autobahn-testsuite).
40-
1. `ci/run.sh` runs the above scripts in order.
37+
1. `ci/fmt.sh` which requires node (specifically prettier).
38+
1. `ci/lint.sh` which requires [shellcheck](https://github.com/koalaman/shellcheck#installing).
39+
1. `ci/test.sh`
40+
1. `ci/run.sh` which runs the above scripts in order.
4141

4242
For coverage details locally, please see `ci/out/coverage.html` after running `ci/test.sh`.
4343

4444
See [ci/image/Dockerfile](ci/image/Dockerfile) for the installation of the CI dependencies on Ubuntu.
4545

46-
You can also run tests normally with `go test` once you have the
47-
[Autobahn Test suite pip package](https://github.com/crossbario/autobahn-testsuite)
48-
installed. `ci/test.sh` just passes a default set of flags to `go test` to collect coverage,
46+
You can also run tests normally with `go test`.
47+
`ci/test.sh` just passes a default set of flags to `go test` to collect coverage,
4948
enable the race detector, run benchmarks and also prettifies the output.
49+
50+
If you pass flags to `ci/test.sh`, it will pass those flags directly to `go test` but will also
51+
collect coverage for you. This is nice for when you don't want to wait for benchmarks
52+
or the race detector but want to have coverage.
53+
54+
Coverage percentage from codecov and the CI scripts will be different because they are calculated differently.

example_echo_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func Example_echo() {
6868
func echoServer(w http.ResponseWriter, r *http.Request) error {
6969
log.Printf("serving %v", r.RemoteAddr)
7070

71-
c, err := websocket.Accept(w, r, websocket.AcceptOptions{
71+
c, err := websocket.Accept(w, r, &websocket.AcceptOptions{
7272
Subprotocols: []string{"echo"},
7373
})
7474
if err != nil {
@@ -128,7 +128,7 @@ func client(url string) error {
128128
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
129129
defer cancel()
130130

131-
c, _, err := websocket.Dial(ctx, url, websocket.DialOptions{
131+
c, _, err := websocket.Dial(ctx, url, &websocket.DialOptions{
132132
Subprotocols: []string{"echo"},
133133
})
134134
if err != nil {

0 commit comments

Comments
 (0)