Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add reqtest.Recorder #124

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion builder_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ import (
"strings"

"github.com/carlmjohnson/requests"
"github.com/carlmjohnson/requests/reqtest"
)

func init() {
http.DefaultClient.Transport = requests.Replay("testdata")
http.DefaultClient.Transport = reqtest.Recorder(reqtest.ModeReplay, nil, "testdata")
}

func Example() {
Expand Down
2 changes: 1 addition & 1 deletion config.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func GzipConfig(level int, h func(gw *gzip.Writer) error) Config {
// which sets the Builder's BaseURL to s.URL
// and the Builder's Client to s.Client().
//
// Deprecated: Use reqtest.Server.
// Deprecated: Use [reqtest.Server].
func TestServerConfig(s *httptest.Server) Config {
return func(rb *Builder) {
rb.
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module github.com/carlmjohnson/requests

go 1.22
go 1.23

require golang.org/x/net v0.33.0
8 changes: 4 additions & 4 deletions recorder.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
// Requests are named according to a hash of their contents.
// Responses are named according to the request that made them.
//
// Deprecated: Use reqtest.Record.
// Deprecated: Use [reqtest.Recorder].
func Record(rt http.RoundTripper, basepath string) Transport {
if rt == nil {
rt = http.DefaultTransport
Expand Down Expand Up @@ -59,7 +59,7 @@ func Record(rt http.RoundTripper, basepath string) Transport {
// responses from text files in basepath.
// Responses are looked up according to a hash of the request.
//
// Deprecated: Use reqtest.Replay.
// Deprecated: Use [reqtest.Recorder].
func Replay(basepath string) Transport {
return ReplayFS(os.DirFS(basepath))
}
Expand All @@ -71,7 +71,7 @@ var errNotFound = errors.New("response not found")
// Responses are looked up according to a hash of the request.
// Response file names may optionally be prefixed with comments for better human organization.
//
// Deprecated: Use reqtest.ReplayFS.
// Deprecated: Use [reqtest.Recorder] and [os.CopyFS].
func ReplayFS(fsys fs.FS) Transport {
return RoundTripFunc(func(req *http.Request) (res *http.Response, err error) {
defer func() {
Expand Down Expand Up @@ -117,7 +117,7 @@ func buildName(b []byte) (reqname, resname string) {
// Requests are named according to a hash of their contents.
// Responses are named according to the request that made them.
//
// Deprecated: Use reqtest.Caching.
// Deprecated: Use [reqtest.Recorder].
func Caching(rt http.RoundTripper, basepath string) Transport {
replay := Replay(basepath).RoundTrip
record := Record(rt, basepath).RoundTrip
Expand Down
40 changes: 40 additions & 0 deletions reqtest/recorder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package reqtest

import (
"net/http"
)

// RecorderMode is an argument type controlling [Recorder].
type RecorderMode int8

//go:generate stringer -type=RecorderMode

// Enum values for type RecorderMode
const (
// Record HTTP requests and responses to text files.
ModeRecord RecorderMode = iota
// Replay responses from pre-recorded text files.
ModeReplay
// Replay responses from pre-recorded files if present,
// otherwise record a new request/response pair.
ModeCache
)

// Recorder returns an HTTP transport that operates in the specified mode.
// Requests and responses are read from or written to
// text files in basepath according to a hash of their contents.
// File names may optionally be prefixed with comments for better human organization.
// The http.RoundTripper is only used in [ModeRecord] and [ModeCache]
// and if nil defaults to [http.DefaultTransport].
func Recorder(mode RecorderMode, rt http.RoundTripper, basepath string) http.RoundTripper {
switch mode {
case ModeReplay:
return Replay(basepath)
case ModeRecord:
return Record(rt, basepath)
case ModeCache:
return Caching(rt, basepath)
default:
panic("invalid reqtest.RecorderMode")
}
}
40 changes: 32 additions & 8 deletions reqtest/recorder_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package reqtest_test
import (
"context"
"fmt"
"os"
"testing/fstest"

"github.com/carlmjohnson/requests"
Expand All @@ -28,26 +29,49 @@ An example response.`
// true
}

func ExampleReplayFS() {
fsys := fstest.MapFS{
"fsys.example - MKIYDwjs.res.txt": &fstest.MapFile{
Data: []byte(`HTTP/1.1 200 OK
func copyToTempDir(m map[string]string) string {
dir, err := os.MkdirTemp("", "")
if err != nil {
panic(err)
}
fsys := make(fstest.MapFS, len(m))
for path, content := range m {
fsys[path] = &fstest.MapFile{
Data: []byte(content),
}
}
if err = os.CopyFS(dir, fsys); err != nil {
panic(err)
}
return dir
}

func ExampleRecorder() {
// Given a directory with the following file
dir := copyToTempDir(map[string]string{
"fsys.example - MKIYDwjs.res.txt": `HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Date: Mon, 24 May 2021 18:48:50 GMT

An example response.`),
},
}
An example response.`,
})
defer os.RemoveAll(dir)

// Make a test transport that reads the directory
tr := reqtest.Recorder(reqtest.ModeReplay, nil, dir)

// And test that it produces the correct response
var s string
const expected = `An example response.`
if err := requests.
URL("http://fsys.example").
Transport(reqtest.ReplayFS(fsys)).
Transport(tr).
ToString(&s).
Fetch(context.Background()); err != nil {
panic(err)
}
fmt.Println(s == expected)

// Output:
// true
}
8 changes: 4 additions & 4 deletions reqtest/recorder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,21 @@ import (
"github.com/carlmjohnson/requests/reqtest"
)

func TestRecordReplay(t *testing.T) {
func TestRecorder(t *testing.T) {
baseTrans := requests.ReplayString(`HTTP/1.1 200 OK

Test Document 1`)
dir := t.TempDir()

var s1, s2 string
err := requests.URL("http://example.com").
Transport(reqtest.Record(baseTrans, dir)).
Transport(reqtest.Recorder(reqtest.ModeRecord, baseTrans, dir)).
ToString(&s1).
Fetch(context.Background())
be.NilErr(t, err)

err = requests.URL("http://example.com").
Transport(reqtest.Replay(dir)).
Transport(reqtest.Recorder(reqtest.ModeReplay, nil, dir)).
ToString(&s2).
Fetch(context.Background())
be.NilErr(t, err)
Expand All @@ -48,7 +48,7 @@ func TestCaching(t *testing.T) {
}
return
}
trans := reqtest.Caching(onceTrans, dir)
trans := reqtest.Recorder(reqtest.ModeCache, onceTrans, dir)
var s1, s2 string
err := requests.URL("http://example.com").
Transport(trans).
Expand Down
25 changes: 25 additions & 0 deletions reqtest/recordermode_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions reqtest/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ func ReplayString(rawResponse string) requests.Transport {
// requests and their responses to text files in basepath.
// Requests are named according to a hash of their contents.
// Responses are named according to the request that made them.
//
// Deprecated: Use [Recorder].
func Record(rt http.RoundTripper, basepath string) requests.Transport {
return requests.Record(rt, basepath)
}
Expand All @@ -25,6 +27,8 @@ func Record(rt http.RoundTripper, basepath string) requests.Transport {
// responses from text files in basepath.
// Responses are looked up according to a hash of the request.
// Response file names may optionally be prefixed with comments for better human organization.
//
// Deprecated: Use [Recorder].
func Replay(basepath string) requests.Transport {
return requests.Replay(basepath)
}
Expand All @@ -33,6 +37,8 @@ func Replay(basepath string) requests.Transport {
// responses from text files in the fs.FS.
// Responses are looked up according to a hash of the request.
// Response file names may optionally be prefixed with comments for better human organization.
//
// Deprecated: Use os.CopyFS and [Recorder].
func ReplayFS(fsys fs.FS) requests.Transport {
return requests.ReplayFS(fsys)
}
Expand All @@ -42,6 +48,8 @@ func ReplayFS(fsys fs.FS) requests.Transport {
// it caches the result of issuing the request with rt in basepath.
// Requests are named according to a hash of their contents.
// Responses are named according to the request that made them.
//
// Deprecated: Use [Recorder].
func Caching(rt http.RoundTripper, basepath string) requests.Transport {
return requests.Caching(rt, basepath)
}
3 changes: 1 addition & 2 deletions reqxml/to_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import (
)

func init() {
http.DefaultClient.Transport = reqtest.Replay("testdata")
// http.DefaultClient.Transport = reqtest.Caching(nil, "testdata")
http.DefaultClient.Transport = reqtest.Recorder(reqtest.ModeReplay, nil, "testdata")
}

func ExampleTo() {
Expand Down
2 changes: 1 addition & 1 deletion transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ var _ Transport = RoundTripFunc(nil)
// ReplayString returns an http.RoundTripper that always responds with a
// request built from rawResponse. It is intended for use in one-off tests.
//
// Deprecated: Use reqtest.ReplayString.
// Deprecated: Use [reqtest.ReplayString].
func ReplayString(rawResponse string) Transport {
return RoundTripFunc(func(req *http.Request) (res *http.Response, err error) {
r := bufio.NewReader(strings.NewReader(rawResponse))
Expand Down
Loading