httpreplay

package
v3.26.0+incompatible Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 22, 2019 License: MPL-2.0 Imports: 19 Imported by: 0

README

HTTP Replay

Synopsis

	// install the hook for HTTP replaying:
	
		if h, ok := client.HTTPClient.(*http.Client); ok {
			_, err := httpreplay.InstallRecorder(h)
			if err != nil {
				return err
			}
		}

func TestMyServiceResource_basic(t *testing.T) {
    // In a unit test, tell the recorder what test we are running
    httpreplay.SetScenario("TestMyServiceResource_basic")
    defer httpreplay.SaveScenario()
    ... testing happens ...
}

Description

This library provides a recording mechanism for the OCI-GO-SDK. It hooks into the 'Transport' layer of the HTTP request, calling through to the real network as needed.

  • bypass (default): Do nothing
  • record: Store the Interaction
  • replay: Load the Interaction file and send back the response

Select the record or replay mode by specifying a build tag to go: -tags <mode>

Functions

InstallRecorder

  • Install hooks into the http.Client to allow the record/replay library to intercept HTTP calls. InstallRecorder tries to be safe if called multiple times, but it is possible to fool it. Best is to only call it once per http.Client.

SetScenario

  • Name the scenario that is about to run.

    Currently, this specifies the filename that the scenario data will be saved into, with a .yaml extension.

    • If -tags record is specified, the requests are written to this file in the SaveScenario call. The Interaction file will be stored in directory ~<prj_path>/record/ with the name passed into.
    • If -tags replay is specified, then a file by that name is immediately read and used for generating replies to network requests.

SaveScenario

  • Save the scenario data.

    Currently, if -tags record is specified, this writes all the recorded requests to the file named in SetScenario.

Record Storage

  • In record mode: After running the test case, the record file will be stored under "oci/record/".
  • In replay mode: Look for the record file under "oci/record/" and throw error if it is not found.

Example usage

  • To run normally: go test
  • Or run 1 specific test case: go test -run <testname>

  • To record interactions: go test -tags record
  • Or to record 1 specific test case: go test -run <testname> -tags record

  • To replay interactions: go test -tags replay
  • Or to replay 1 specific test case: go test -run <testname> -tags replay
Example Output

Run with recording turned on, the test portion takes 2411 seconds:

> go test -v -timeout 120m -run TestResourceCoreImageTestSuite -tags record
=== RUN   TestResourceCoreImageTestSuite
=== RUN   TestResourceCoreImageTestSuite/TestAccResourceCoreImage_basic
=== RUN   TestResourceCoreImageTestSuite/TestAccResourceCoreImage_createFromExport_objectStorageTuple
=== RUN   TestResourceCoreImageTestSuite/TestAccResourceCoreImage_createFromExport_objectStorageUri
--- PASS: TestResourceCoreImageTestSuite (2411.14s)
    --- PASS: TestResourceCoreImageTestSuite/TestAccResourceCoreImage_basic (2410.58s)
    --- SKIP: TestResourceCoreImageTestSuite/TestAccResourceCoreImage_createFromExport_objectStorageTuple (0.00s)
        core_image_resource_test.go:191: Long running test, requires per tenancy namespace + bucket + image export object to run
    --- SKIP: TestResourceCoreImageTestSuite/TestAccResourceCoreImage_createFromExport_objectStorageUri (0.00s)
        core_image_resource_test.go:155: Long running test, requires exported image available via public url
PASS

Now that we have a recording, run in replay mode, note that it is only 4.09 seconds:

> go test -v -run TestResourceCoreImageTestSuite -tags replay
=== RUN   TestResourceCoreImageTestSuite
=== RUN   TestResourceCoreImageTestSuite/TestAccResourceCoreImage_basic
=== RUN   TestResourceCoreImageTestSuite/TestAccResourceCoreImage_createFromExport_objectStorageTuple
=== RUN   TestResourceCoreImageTestSuite/TestAccResourceCoreImage_createFromExport_objectStorageUri
--- PASS: TestResourceCoreImageTestSuite (4.09s)
    --- PASS: TestResourceCoreImageTestSuite/TestAccResourceCoreImage_basic (3.60s)
    --- SKIP: TestResourceCoreImageTestSuite/TestAccResourceCoreImage_createFromExport_objectStorageTuple (0.00s)
        core_image_resource_test.go:191: Long running test, requires per tenancy namespace + bucket + image export object to run
    --- SKIP: TestResourceCoreImageTestSuite/TestAccResourceCoreImage_createFromExport_objectStorageUri (0.00s)
        core_image_resource_test.go:155: Long running test, requires exported image available via public url
PASS

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrInteractionNotFound indicates that a requested
	// interaction was not found in the scenario file
	ErrInteractionNotFound = errors.New("Requested interaction not found")
)

Functions

func DefaultLogger

func DefaultLogger() *log.Logger

func ModeRecordReplay

func ModeRecordReplay() bool

ModeRecordReplay returns true in record and replay

func SaveScenario

func SaveScenario() error

SaveScenario saves the recorded service calls for the current scenario

func SetDebugLogger

func SetDebugLogger(logger *log.Logger)

func SetScenario

func SetScenario(name string) error

SetScenario lets the recorder know which test is currently executing

func ShouldRetryImmediately

func ShouldRetryImmediately() bool

ShouldRetryImmediately returns true if replaying

Types

type HTTPRecordingClient

type HTTPRecordingClient interface {
	Do(req *http.Request) (*http.Response, error)
}

HTTPRecordingClient wraps the execution of a http request, adding record/replay functionality. It is meant to be compatible with oci-go-sdk's client.HTTPRequestDispatcher interface.

func InstallRecorder

func InstallRecorder(client *http.Client) (HTTPRecordingClient, error)

InstallRecorder does no-op.

func InstallRecorderForRecodReplay

func InstallRecorderForRecodReplay(client *http.Client, recorder *Recorder) (HTTPRecordingClient, error)

type Interaction

type Interaction struct {
	Index    int `yaml:"index"`
	Uses     int `yaml:"uses"`
	Request  `yaml:"request"`
	Response `yaml:"response"`
}

Interaction type contains a pair of request/response for a single HTTP interaction between a client and a server

type Interactions

type Interactions []Interaction

type Matcher

type Matcher func(int, *Request, *Request) bool

Matcher function returns true when the actual request matches a single HTTP interaction's request according to the function's own criteria.

type Mode

type Mode int

Mode represents recording/playback mode

const (
	ModeRecording Mode = iota
	ModeReplaying
	ModeDisabled
)

Recorder states

type Recorder

type Recorder struct {
	// contains filtered or unexported fields
}

Recorder represents a type used to record and replay client and server interactions

func NewRecorder

func NewRecorder(scenarioName string) (*Recorder, error)

NewRecorder creates a new recorder

func NewRecorderAsMode

func NewRecorderAsMode(scenarioName string, mode Mode) (*Recorder, error)

NewRecorderAsMode creates a new recorder in the specified mode

func (*Recorder) CancelRequest

func (r *Recorder) CancelRequest(req *http.Request, realTransport http.RoundTripper)

CancelRequest implements the github.com/coreos/etcd/client.CancelableTransport interface

func (*Recorder) HookTransport

func (r *Recorder) HookTransport(client *http.Client) error

HookTransport makes a new transport and chains the one passed in with it, returning the new one

func (*Recorder) RoundTrip

func (r *Recorder) RoundTrip(req *http.Request, realTransport http.RoundTripper) (*http.Response, error)

RoundTrip implements the http.RoundTripper interface

func (*Recorder) SetMatcher

func (r *Recorder) SetMatcher(matcher Matcher)

SetMatcher sets a function to match requests against recorded HTTP interactions.

func (*Recorder) SetTransformer

func (r *Recorder) SetTransformer(t Transformer)

SetTransformer can be used to override the default (no-op) transformer

func (*Recorder) Stop

func (r *Recorder) Stop() error

Stop is used to stop the recorder and save any recorded interactions

type Request

type Request struct {
	// Body of request
	Body string `yaml:"body"`

	// BodyParsed is parsed from body json
	BodyParsed interface{} `yaml:"bodyParsed"`

	// Form values
	Form url.Values `yaml:"form"`

	// Request headers
	Headers http.Header `yaml:"headers"`

	// Request URL
	URL string `yaml:"url"`

	// Request method
	Method string `yaml:"method"`
}

Request represents a client request as recorded in the scenario file

func ConverRequestWithFullPath

func ConverRequestWithFullPath(r Request) (Request, error)

type Response

type Response struct {
	// Body of responsen
	Body string `yaml:"body"`

	// BodyParsed is parsed from body json
	BodyParsed interface{} `yaml:"bodyParsed"`

	// Response headers
	Headers http.Header `yaml:"headers"`

	// Response status message
	Status string `yaml:"status"`

	// Response status code
	Code int `yaml:"code"`

	// Response duration (something like "100ms" or "10s")
	Duration string `yaml:"duration"`
}

Response represents a server response as recorded in the scenario file

type Scenario

type Scenario struct {
	// Name of the scenario
	Name string `yaml:"-"`

	// File name of the scenario as written on disk
	File string `yaml:"-"`

	// Scenario format version
	Version int `yaml:"version"`

	// Mutex to lock accessing Interactions. omitempty is set
	// to prevent the mutex appearing in the recorded YAML.
	Mu sync.RWMutex `yaml:"mu,omitempty"`
	// Interactions between client and server
	Interactions `yaml:"interactions"`

	// Matches actual request with interaction requests.
	Matcher `yaml:"-"`
	// contains filtered or unexported fields
}

Scenario type

func Load

func Load(name string) (*Scenario, error)

Load reads a scenario file from disk

func NewScenario

func NewScenario(name string) *Scenario

NewScenario creates a new empty Scenario

func (*Scenario) AddInteraction

func (s *Scenario) AddInteraction(i *Interaction)

AddInteraction appends a new interaction to the scenario

func (*Scenario) GetInteraction

func (s *Scenario) GetInteraction(r Request) (*Interaction, error)

GetInteraction retrieves a recorded request/response interaction

func (*Scenario) GetInteractionWithBody

func (s *Scenario) GetInteractionWithBody(r Request) (*Interaction, error)

Get Interaction with body by compare the body of request and Interaction

func (*Scenario) GetInteractionWithBodyFromList

func (s *Scenario) GetInteractionWithBodyFromList(r Request, list []*Interaction) (*Interaction, error)

func (*Scenario) GetInteractionWithFullPath

func (s *Scenario) GetInteractionWithFullPath(r Request) (*Interaction, error)

func (*Scenario) GetInteractionWithQueryString

func (s *Scenario) GetInteractionWithQueryString(r Request) (*Interaction, error)

Get match Interaction by compare the query string in request

func (*Scenario) GetInteractionWithQueryStringFromList

func (s *Scenario) GetInteractionWithQueryStringFromList(r Request, list []*Interaction) (*Interaction, error)

func (*Scenario) Reset

func (s *Scenario) Reset()

Reset returns us to the beginning of the scenario

func (*Scenario) Save

func (s *Scenario) Save() error

Save writes the scenario data on disk for future re-use

type Transformer

type Transformer func(*Request, Interaction, *Response)

Transformer converts a request and a saved interaction into a result. The Interaction is passed by value to suggest that it should not be modified.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL