Skip to content

Commit e0585cc

Browse files
Add reqtest, reqhtml; deprecate requests.ToHTML and testing transports (#115)
* Add reqtest, reqhtml; deprecate requests.ToHTML and testing transports * reqtest: Add package doc * reqtest: Rename to reqtest.Server and improve docs * reqhtml.Body: Better doc string * Docs: Better docs for reqtest.Replay.
1 parent caed42a commit e0585cc

15 files changed

+365
-5
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -225,14 +225,14 @@ fmt.Println(u.String()) // https://dev1.example.com/get?a=1&b=3&c=4
225225
// record a request to the file system
226226
var s1, s2 string
227227
err := requests.URL("http://example.com").
228-
Transport(requests.Record(nil, "somedir")).
228+
Transport(reqtest.Record(nil, "somedir")).
229229
ToString(&s1).
230230
Fetch(ctx)
231231
check(err)
232232

233233
// now replay the request in tests
234234
err = requests.URL("http://example.com").
235-
Transport(requests.Replay("somedir")).
235+
Transport(reqtest.Replay("somedir")).
236236
ToString(&s2).
237237
Fetch(ctx)
238238
check(err)

config.go

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ func GzipConfig(level int, h func(gw *gzip.Writer) error) Config {
3434
// TestServerConfig returns a Config
3535
// which sets the Builder's BaseURL to s.URL
3636
// and the Builder's Client to s.Client().
37+
//
38+
// Deprecated: Use reqtest.Server.
3739
func TestServerConfig(s *httptest.Server) Config {
3840
return func(rb *Builder) {
3941
rb.

handler.go

+2
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ func ToBufioScanner(f func(r *bufio.Scanner) error) ResponseHandler {
9494
}
9595

9696
// ToHTML parses the page with x/net/html.Parse.
97+
//
98+
// Deprecated: Use reqhtml.To.
9799
func ToHTML(n *html.Node) ResponseHandler {
98100
return ToBufioReader(func(r *bufio.Reader) error {
99101
n2, err := html.Parse(r)

recorder.go

+8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import (
1818
// requests and their responses to text files in basepath.
1919
// Requests are named according to a hash of their contents.
2020
// Responses are named according to the request that made them.
21+
//
22+
// Deprecated: Use reqtest.Record.
2123
func Record(rt http.RoundTripper, basepath string) Transport {
2224
if rt == nil {
2325
rt = http.DefaultTransport
@@ -56,6 +58,8 @@ func Record(rt http.RoundTripper, basepath string) Transport {
5658
// Replay returns an http.RoundTripper that reads its
5759
// responses from text files in basepath.
5860
// Responses are looked up according to a hash of the request.
61+
//
62+
// Deprecated: Use reqtest.Replay.
5963
func Replay(basepath string) Transport {
6064
return ReplayFS(os.DirFS(basepath))
6165
}
@@ -66,6 +70,8 @@ var errNotFound = errors.New("response not found")
6670
// responses from text files in the fs.FS.
6771
// Responses are looked up according to a hash of the request.
6872
// Response file names may optionally be prefixed with comments for better human organization.
73+
//
74+
// Deprecated: Use reqtest.ReplayFS.
6975
func ReplayFS(fsys fs.FS) Transport {
7076
return RoundTripFunc(func(req *http.Request) (res *http.Response, err error) {
7177
defer func() {
@@ -110,6 +116,8 @@ func buildName(b []byte) (reqname, resname string) {
110116
// it caches the result of issuing the request with rt in basepath.
111117
// Requests are named according to a hash of their contents.
112118
// Responses are named according to the request that made them.
119+
//
120+
// Deprecated: Use reqtest.Caching.
113121
func Caching(rt http.RoundTripper, basepath string) Transport {
114122
replay := Replay(basepath).RoundTrip
115123
record := Record(rt, basepath).RoundTrip

recorder_test.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@ import (
1313
)
1414

1515
func TestRecordReplay(t *testing.T) {
16+
baseTrans := requests.ReplayString(`HTTP/1.1 200 OK
17+
18+
Test Document 1`)
1619
dir := t.TempDir()
1720

1821
var s1, s2 string
1922
err := requests.URL("http://example.com").
20-
Transport(requests.Record(http.DefaultTransport, dir)).
23+
Transport(requests.Record(baseTrans, dir)).
2124
ToString(&s1).
2225
Fetch(context.Background())
2326
be.NilErr(t, err)
@@ -28,6 +31,7 @@ func TestRecordReplay(t *testing.T) {
2831
Fetch(context.Background())
2932
be.NilErr(t, err)
3033
be.Equal(t, s1, s2)
34+
be.Equal(t, "Test Document 1", s1)
3135
}
3236

3337
func TestCaching(t *testing.T) {

reqhtml/html.go

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Package reqhtml contains utilities for sending and receiving x/net/html objects.
2+
package reqhtml
3+
4+
import (
5+
"io"
6+
7+
"github.com/carlmjohnson/requests"
8+
"golang.org/x/net/html"
9+
)
10+
11+
// To decodes a response as an html document.
12+
func To(n *html.Node) requests.ResponseHandler {
13+
return requests.ToHTML(n)
14+
}
15+
16+
// Body sets the requests.Builder's request body to the HTML document.
17+
// It also sets ContentType to "text/html"
18+
// if it is not otherwise set.
19+
func Body(n *html.Node) requests.Config {
20+
return func(rb *requests.Builder) {
21+
rb.
22+
Body(requests.BodyWriter(func(w io.Writer) error {
23+
return html.Render(w, n)
24+
})).
25+
HeaderOptional("context-type", "text/html")
26+
}
27+
}

reqhtml/html_test.go

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package reqhtml_test
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/http"
7+
"net/http/httputil"
8+
9+
"github.com/carlmjohnson/requests"
10+
"github.com/carlmjohnson/requests/reqhtml"
11+
"github.com/carlmjohnson/requests/reqtest"
12+
"golang.org/x/net/html"
13+
"golang.org/x/net/html/atom"
14+
)
15+
16+
func init() {
17+
http.DefaultTransport = reqtest.ReplayString(`HTTP/1.1 200 OK
18+
19+
<a href="https://www.iana.org/domains/example"></a>`)
20+
}
21+
22+
func ExampleTo() {
23+
var doc html.Node
24+
err := requests.
25+
URL("http://example.com").
26+
Handle(reqhtml.To(&doc)).
27+
Fetch(context.Background())
28+
if err != nil {
29+
fmt.Println("could not connect to example.com:", err)
30+
}
31+
var f func(*html.Node)
32+
f = func(n *html.Node) {
33+
if n.DataAtom == atom.A {
34+
for _, attr := range n.Attr {
35+
if attr.Key == "href" {
36+
fmt.Println("link:", attr.Val)
37+
}
38+
}
39+
}
40+
for c := n.FirstChild; c != nil; c = c.NextSibling {
41+
f(c)
42+
}
43+
}
44+
f(&doc)
45+
// Output:
46+
// link: https://www.iana.org/domains/example
47+
}
48+
49+
func ExampleBody() {
50+
link := html.Node{
51+
Type: html.ElementNode,
52+
Data: "a",
53+
Attr: []html.Attribute{
54+
{Key: "href", Val: "http://example.com"},
55+
},
56+
}
57+
text := html.Node{
58+
Type: html.TextNode,
59+
Data: "Hello, World!",
60+
}
61+
link.AppendChild(&text)
62+
63+
req, err := requests.
64+
URL("http://example.com").
65+
Config(reqhtml.Body(&link)).
66+
Request(context.Background())
67+
b, err := httputil.DumpRequest(req, true)
68+
if err != nil {
69+
panic(err)
70+
}
71+
fmt.Printf("%q\n", b)
72+
73+
// Output:
74+
// "POST / HTTP/1.1\r\nHost: example.com\r\nContext-Type: text/html\r\n\r\n<a href=\"http://example.com\">Hello, World!</a>"
75+
}

reqtest/doc.go

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package reqtest contains helpers for writing tests of HTTP clients and servers.
2+
package reqtest

reqtest/recorder_example_test.go

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package reqtest_test
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing/fstest"
7+
8+
"github.com/carlmjohnson/requests"
9+
"github.com/carlmjohnson/requests/reqtest"
10+
)
11+
12+
func ExampleReplayString() {
13+
const res = `HTTP/1.1 200 OK
14+
15+
An example response.`
16+
17+
var s string
18+
const expected = `An example response.`
19+
if err := requests.
20+
URL("http://response.example").
21+
Transport(reqtest.ReplayString(res)).
22+
ToString(&s).
23+
Fetch(context.Background()); err != nil {
24+
panic(err)
25+
}
26+
fmt.Println(s == expected)
27+
// Output:
28+
// true
29+
}
30+
31+
func ExampleReplayFS() {
32+
fsys := fstest.MapFS{
33+
"fsys.example - MKIYDwjs.res.txt": &fstest.MapFile{
34+
Data: []byte(`HTTP/1.1 200 OK
35+
Content-Type: text/plain; charset=UTF-8
36+
Date: Mon, 24 May 2021 18:48:50 GMT
37+
38+
An example response.`),
39+
},
40+
}
41+
var s string
42+
const expected = `An example response.`
43+
if err := requests.
44+
URL("http://fsys.example").
45+
Transport(reqtest.ReplayFS(fsys)).
46+
ToString(&s).
47+
Fetch(context.Background()); err != nil {
48+
panic(err)
49+
}
50+
fmt.Println(s == expected)
51+
// Output:
52+
// true
53+
}

reqtest/recorder_test.go

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package reqtest_test
2+
3+
import (
4+
"context"
5+
"io"
6+
"net/http"
7+
"os"
8+
"strings"
9+
"testing"
10+
11+
"github.com/carlmjohnson/requests"
12+
"github.com/carlmjohnson/requests/internal/be"
13+
"github.com/carlmjohnson/requests/reqtest"
14+
)
15+
16+
func TestRecordReplay(t *testing.T) {
17+
baseTrans := requests.ReplayString(`HTTP/1.1 200 OK
18+
19+
Test Document 1`)
20+
dir := t.TempDir()
21+
22+
var s1, s2 string
23+
err := requests.URL("http://example.com").
24+
Transport(reqtest.Record(baseTrans, dir)).
25+
ToString(&s1).
26+
Fetch(context.Background())
27+
be.NilErr(t, err)
28+
29+
err = requests.URL("http://example.com").
30+
Transport(reqtest.Replay(dir)).
31+
ToString(&s2).
32+
Fetch(context.Background())
33+
be.NilErr(t, err)
34+
be.Equal(t, s1, s2)
35+
be.Equal(t, "Test Document 1", s1)
36+
}
37+
38+
func TestCaching(t *testing.T) {
39+
dir := t.TempDir()
40+
hasRun := false
41+
content := "some content"
42+
var onceTrans requests.RoundTripFunc = func(req *http.Request) (res *http.Response, err error) {
43+
be.False(t, hasRun)
44+
hasRun = true
45+
res = &http.Response{
46+
StatusCode: http.StatusOK,
47+
Body: io.NopCloser(strings.NewReader(content)),
48+
}
49+
return
50+
}
51+
trans := reqtest.Caching(onceTrans, dir)
52+
var s1, s2 string
53+
err := requests.URL("http://example.com").
54+
Transport(trans).
55+
ToString(&s1).
56+
Fetch(context.Background())
57+
be.NilErr(t, err)
58+
err = requests.URL("http://example.com").
59+
Transport(trans).
60+
ToString(&s2).
61+
Fetch(context.Background())
62+
be.NilErr(t, err)
63+
be.Equal(t, content, s1)
64+
be.Equal(t, s1, s2)
65+
66+
entries, err := os.ReadDir(dir)
67+
be.NilErr(t, err)
68+
be.Equal(t, 2, len(entries))
69+
}

reqtest/server.go

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package reqtest
2+
3+
import (
4+
"net/http/httptest"
5+
6+
"github.com/carlmjohnson/requests"
7+
)
8+
9+
// Server takes an httptest.Server and returns a requests.Config
10+
// which sets the requests.Builder's BaseURL to s.URL
11+
// and the requests.Builder's Client to s.Client().
12+
func Server(s *httptest.Server) requests.Config {
13+
return requests.TestServerConfig(s)
14+
}

reqtest/server_example_test.go

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package reqtest_test
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/http"
7+
"net/http/httptest"
8+
9+
"github.com/carlmjohnson/requests"
10+
"github.com/carlmjohnson/requests/reqtest"
11+
)
12+
13+
func ExampleServer() {
14+
// Create an httptest.Server for your project's router
15+
mux := http.NewServeMux()
16+
mux.HandleFunc("/greeting", func(w http.ResponseWriter, r *http.Request) {
17+
fmt.Fprintf(w, "Hello, world!")
18+
})
19+
mux.HandleFunc("/salutation", func(w http.ResponseWriter, r *http.Request) {
20+
fmt.Fprintf(w, "Howdy, planet!")
21+
})
22+
23+
srv := httptest.NewServer(mux)
24+
defer srv.Close()
25+
26+
// Now test that the handler has the expected return values
27+
{
28+
var s string
29+
err := requests.
30+
New(reqtest.Server(srv)).
31+
Path("/greeting").
32+
ToString(&s).
33+
Fetch(context.Background())
34+
if err != nil {
35+
fmt.Println("Error!", err)
36+
}
37+
fmt.Println(s) // Hello, world!
38+
}
39+
{
40+
var s string
41+
err := requests.
42+
New(reqtest.Server(srv)).
43+
Path("/salutation").
44+
ToString(&s).
45+
Fetch(context.Background())
46+
if err != nil {
47+
fmt.Println("Error!", err)
48+
}
49+
fmt.Println(s) // Howdy, planet!
50+
}
51+
// Output:
52+
// Hello, world!
53+
// Howdy, planet!
54+
}

0 commit comments

Comments
 (0)