Skip to content

Commit

Permalink
Add DiscardOnSave field to Interaction type
Browse files Browse the repository at this point in the history
The DiscardOnSave field may be set to true by hooks, in order to
discard an interaction before saving it on disk.

See issue #80 for some background info.
  • Loading branch information
dnaeon committed Oct 25, 2022
1 parent db4802a commit 79e4390
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 3 deletions.
33 changes: 30 additions & 3 deletions cassette/cassette.go
Expand Up @@ -123,10 +123,23 @@ type Response struct {
// Interaction type contains a pair of request/response for a
// single HTTP interaction between a client and a server
type Interaction struct {
ID int `yaml:"id"`
Request Request `yaml:"request"`
// ID is the id of the interaction
ID int `yaml:"id"`

// Request is the recorded request
Request Request `yaml:"request"`

// Response is the recorded response
Response Response `yaml:"response"`
replayed bool `yaml:"-"`

// DiscardOnSave if set to true will discard the interaction
// as a whole and it will not be part of the final
// interactions when saving the cassette on disk.
DiscardOnSave bool `yaml:"-"`

// replayed is true when this interaction has been played
// already.
replayed bool `yaml:"-"`
}

// GetHTTPRequest converts the recorded interaction request to
Expand Down Expand Up @@ -301,6 +314,20 @@ func (c *Cassette) Save() error {
}
}

// Filter out interactions which should be discarded. While
// discarding interactions we should also fix the interaction
// IDs, so that we don't introduce gaps in the final results.
nextId := 0
interactions := make([]*Interaction, 0)
for _, i := range c.Interactions {
if !i.DiscardOnSave {
i.ID = nextId
interactions = append(interactions, i)
nextId += 1
}
}
c.Interactions = interactions

// Marshal to YAML and save interactions
data, err := yaml.Marshal(c)
if err != nil {
Expand Down
79 changes: 79 additions & 0 deletions recorder/recorder_test.go
Expand Up @@ -1225,3 +1225,82 @@ func TestInvalidRecorderMode(t *testing.T) {
t.Fatal("expected recorder to fail with invalid mode")
}
}

func TestDiscardInteractionsOnSave(t *testing.T) {
tests := []testCase{
{
method: http.MethodPost,
body: "foo",
wantBody: "POST go-vcr\nfoo",
wantStatus: http.StatusOK,
wantContentLength: 15,
path: "/api/v1/foo",
},
{
method: http.MethodPost,
body: "bar",
wantBody: "POST go-vcr\nbar",
wantStatus: http.StatusOK,
wantContentLength: 15,
path: "/api/v1/bar",
},
}

server := newEchoHttpServer()
serverUrl := server.URL

cassPath, err := newCassettePath("test_discard_interactions_on_save")
if err != nil {
t.Fatal(err)
}

// Create recorder
rec, err := recorder.New(cassPath)
if err != nil {
t.Fatal(err)
}

if rec.Mode() != recorder.ModeRecordOnce {
t.Fatal("recorder is not in the correct mode")
}

if rec.IsRecording() != true {
t.Fatal("recorder is not recording")
}

// The following hook function will be used to determine
// whether an interaction is to be discarded when saving the
// cassette on disk.
hook := func(i *cassette.Interaction) error {
if i.Request.Method == http.MethodPost && i.Request.Body == "foo" {
i.DiscardOnSave = true
}

return nil
}
rec.AddHook(hook, recorder.AfterCaptureHook)

ctx := context.Background()
client := rec.GetDefaultClient()
for _, test := range tests {
if err := test.run(client, ctx, serverUrl); err != nil {
t.Fatal(err)
}
}

// Stop recorder and verify cassette
rec.Stop()

cass, err := cassette.Load(cassPath)
if err != nil {
t.Fatal(err)
}

// We should have one interaction less than our test cases
// when reading the cassette from disk.
wantInteractions := len(tests) - 1
gotInteractions := len(cass.Interactions)
if wantInteractions != gotInteractions {
t.Fatalf("expected %d interactions, got %d", wantInteractions, gotInteractions)
}
}

0 comments on commit 79e4390

Please sign in to comment.