Skip to content

Commit ab572ec

Browse files
authored
test: fail fast on missing VCR cassettes by converting errors to 404 (#2816)
If a VCR cassette or a specific interaction is missing, the OSV client would previously retry the request 4 times with exponential backoff, causing significant delays in test execution (over 20 seconds). This change wraps the VCR client transport in tests to intercept the "requested interaction not found" error (using `errors.Is` with `cassette.ErrInteractionNotFound` for robust matching) and convert it to a "404 Not Found" response. Since the OSV client does not retry on 404 status codes, this allows tests to fail immediately (~0.02s) when a cassette or interaction is missing while still preserving retry behavior for genuine transient network errors. This approach avoids modifying the core `osvscanner` library API or adding new CLI flags. - Implemented `vcrErrorWrappingTransport` in `internal/testcmd/vcr.go` - Wrapped the VCR client transport in `InsertCassette` - Used `errors.Is` with `cassette.ErrInteractionNotFound` for robust error matching
1 parent 57b8b78 commit ab572ec

1 file changed

Lines changed: 25 additions & 1 deletion

File tree

  • cmd/osv-scanner/internal/testcmd

cmd/osv-scanner/internal/testcmd/vcr.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package testcmd
33
import (
44
"bytes"
55
"cmp"
6+
"errors"
67
"fmt"
78
"io"
89
"net/http"
@@ -173,7 +174,10 @@ func InsertCassette(t *testing.T) *http.Client {
173174
sortCassetteInteractions(t, path)
174175
})
175176

176-
return r.GetDefaultClient()
177+
client := r.GetDefaultClient()
178+
client.Transport = &vcrErrorWrappingTransport{wrapper: client.Transport}
179+
180+
return client
177181
}
178182

179183
// sortCassetteInteractions reorders the interactions in the given cassette, based
@@ -255,3 +259,23 @@ func matchBody(r *http.Request, i cassette.Request) bool {
255259

256260
return true
257261
}
262+
263+
type vcrErrorWrappingTransport struct {
264+
wrapper http.RoundTripper
265+
}
266+
267+
func (t *vcrErrorWrappingTransport) RoundTrip(req *http.Request) (*http.Response, error) {
268+
resp, err := t.wrapper.RoundTrip(req)
269+
if err != nil && errors.Is(err, cassette.ErrInteractionNotFound) {
270+
// Convert VCR error to a 404 response to avoid retries by the client
271+
return &http.Response{
272+
StatusCode: http.StatusNotFound,
273+
Status: "404 Not Found (VCR: requested interaction not found)",
274+
Body: io.NopCloser(strings.NewReader("VCR: requested interaction not found")),
275+
Header: make(http.Header),
276+
Request: req,
277+
}, nil
278+
}
279+
280+
return resp, err
281+
}

0 commit comments

Comments
 (0)