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

+3-3
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

+2-2
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

+6-2
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

+33
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

+2-3
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

+22-7
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

+10-2
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

+49
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

+12-7
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

+2-2
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 {

example_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
// message from the client and then closes the connection.
1515
func ExampleAccept() {
1616
fn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
17-
c, err := websocket.Accept(w, r, websocket.AcceptOptions{})
17+
c, err := websocket.Accept(w, r, nil)
1818
if err != nil {
1919
log.Println(err)
2020
return
@@ -46,7 +46,7 @@ func ExampleDial() {
4646
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
4747
defer cancel()
4848

49-
c, _, err := websocket.Dial(ctx, "ws://localhost:8080", websocket.DialOptions{})
49+
c, _, err := websocket.Dial(ctx, "ws://localhost:8080", nil)
5050
if err != nil {
5151
log.Fatal(err)
5252
}
@@ -64,7 +64,7 @@ func ExampleDial() {
6464
// on which you will only write and do not expect to read data messages.
6565
func Example_writeOnly() {
6666
fn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
67-
c, err := websocket.Accept(w, r, websocket.AcceptOptions{})
67+
c, err := websocket.Accept(w, r, nil)
6868
if err != nil {
6969
log.Println(err)
7070
return

export_test.go

+25-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
11
package websocket
22

3-
var Compute = handleSecWebSocketKey
3+
import (
4+
"context"
5+
)
6+
7+
type Addr = websocketAddr
8+
9+
const OPClose = opClose
10+
const OPBinary = opBinary
11+
const OPPing = opPing
12+
const OPContinuation = opContinuation
13+
14+
func (c *Conn) WriteFrame(ctx context.Context, fin bool, opcode opcode, p []byte) (int, error) {
15+
return c.writeFrame(ctx, fin, opcode, p)
16+
}
17+
18+
func (c *Conn) WriteHalfFrame(ctx context.Context) (int, error) {
19+
return c.realWriteFrame(ctx, header{
20+
opcode: opBinary,
21+
payloadLength: 5,
22+
}, make([]byte, 10))
23+
}
24+
25+
func (c *Conn) Flush() error {
26+
return c.bw.Flush()
27+
}

go.mod

+1-3
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ require (
1313
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
1414
golang.org/x/tools v0.0.0-20190429184909-35c670923e21
1515
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522
16-
gotest.tools/gotestsum v0.3.5
16+
gotest.tools/gotestsum v0.3.6-0.20190825182939-fc6cb5870c52
1717
mvdan.cc/sh v2.6.4+incompatible
1818
)
19-
20-
replace gotest.tools/gotestsum => github.com/nhooyr/gotestsum v0.3.6-0.20190821172136-aaabbb33254b

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRU
2222
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
2323
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
2424
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
25-
github.com/nhooyr/gotestsum v0.3.6-0.20190821172136-aaabbb33254b h1:t6DbmxEtGMM72Uhs638nBOyK7tjsrDwoMfYO1EfQdFE=
26-
github.com/nhooyr/gotestsum v0.3.6-0.20190821172136-aaabbb33254b/go.mod h1:Mnf3e5FUzXbkCfynWBGOwLssY7gTQgCHObK9tMpAriY=
2725
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
2826
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
2927
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -85,5 +83,7 @@ gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
8583
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
8684
gotest.tools v2.1.0+incompatible h1:5USw7CrJBYKqjg9R7QlA6jzqZKEAtvW82aNmsxxGPxw=
8785
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
86+
gotest.tools/gotestsum v0.3.6-0.20190825182939-fc6cb5870c52 h1:Qr31uPFyjpOhAgRfKV4ATUnknnLT2X7HFjqwkstdbbE=
87+
gotest.tools/gotestsum v0.3.6-0.20190825182939-fc6cb5870c52/go.mod h1:Mnf3e5FUzXbkCfynWBGOwLssY7gTQgCHObK9tMpAriY=
8888
mvdan.cc/sh v2.6.4+incompatible h1:eD6tDeh0pw+/TOTI1BBEryZ02rD2nMcFsgcvde7jffM=
8989
mvdan.cc/sh v2.6.4+incompatible/go.mod h1:IeeQbZq+x2SUGBensq/jge5lLQbS3XT2ktyp3wrt4x8=

0 commit comments

Comments
 (0)