feat: classify extraction store failures
This commit is contained in:
parent
3da836aeff
commit
1785f4505a
@ -364,7 +364,11 @@ func TestHandlerExtractMapsErrorsToProblemResponsesWithoutSensitiveLeakage(t *te
|
|||||||
name: "extraction unavailable",
|
name: "extraction unavailable",
|
||||||
requestBody: `{"count":1}`,
|
requestBody: `{"count":1}`,
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
extractErr: controllerExtraction.ErrUnavailable,
|
extractErr: errors.Join(
|
||||||
|
controllerExtraction.ErrUnavailable,
|
||||||
|
domainExtraction.ErrStoreUnavailable,
|
||||||
|
errors.New("redis dial failed: TOKEN"),
|
||||||
|
),
|
||||||
wantStatus: http.StatusServiceUnavailable,
|
wantStatus: http.StatusServiceUnavailable,
|
||||||
wantCode: "SERVICE_UNAVAILABLE",
|
wantCode: "SERVICE_UNAVAILABLE",
|
||||||
},
|
},
|
||||||
@ -410,7 +414,8 @@ func TestHandlerExtractMapsErrorsToProblemResponsesWithoutSensitiveLeakage(t *te
|
|||||||
if problem.Status != test.wantStatus || problem.Code != test.wantCode {
|
if problem.Status != test.wantStatus || problem.Code != test.wantCode {
|
||||||
t.Fatalf("problem = %+v", problem)
|
t.Fatalf("problem = %+v", problem)
|
||||||
}
|
}
|
||||||
if body := response.Body.String(); strings.Contains(body, `"password"`) || strings.Contains(body, "secret") {
|
if body := response.Body.String(); strings.Contains(body, `"password"`) ||
|
||||||
|
strings.Contains(body, "secret") || strings.Contains(body, "redis") || strings.Contains(body, "TOKEN") {
|
||||||
t.Fatalf("error body leaked sensitive data: %s", body)
|
t.Fatalf("error body leaked sensitive data: %s", body)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -140,6 +140,9 @@ func (s *Service) Extract(ctx context.Context, request Request) (Response, error
|
|||||||
Upstreams: append([]string(nil), request.Filters.Upstreams...),
|
Upstreams: append([]string(nil), request.Filters.Upstreams...),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, domain.ErrStoreUnavailable) {
|
||||||
|
return response, errors.Join(ErrUnavailable, err)
|
||||||
|
}
|
||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -145,6 +145,27 @@ func TestServiceUsesSourceIdentityForEphemeralIdempotency(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestServiceClassifiesStoreUnavailableAndPreservesCause(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
storeErr := errors.Join(domain.ErrStoreUnavailable, errors.New("redis dial failed: TOKEN"))
|
||||||
|
service, err := NewService(&recordingStore{err: storeErr}, Policy{
|
||||||
|
MaxCountPerRequest: 1,
|
||||||
|
DefaultFulfillment: domain.Partial,
|
||||||
|
}, allowAllAdmission{}, time.Now)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewService(): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = service.Extract(context.Background(), Request{
|
||||||
|
RequestID: "req-1",
|
||||||
|
ClientID: "client-1",
|
||||||
|
Count: 1,
|
||||||
|
})
|
||||||
|
if !errors.Is(err, ErrUnavailable) || !errors.Is(err, domain.ErrStoreUnavailable) {
|
||||||
|
t.Fatalf("Extract() error = %v, want unavailable classification with store cause", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestServiceIdempotentReplayKeepsOriginalExtractionTime(t *testing.T) {
|
func TestServiceIdempotentReplayKeepsOriginalExtractionTime(t *testing.T) {
|
||||||
firstTime := time.Date(2026, 7, 28, 12, 0, 0, 0, time.UTC)
|
firstTime := time.Date(2026, 7, 28, 12, 0, 0, 0, time.UTC)
|
||||||
secondTime := firstTime.Add(time.Minute)
|
secondTime := firstTime.Add(time.Minute)
|
||||||
|
|||||||
@ -24,6 +24,7 @@ var (
|
|||||||
ErrInsufficientProxies = errors.New("insufficient proxies")
|
ErrInsufficientProxies = errors.New("insufficient proxies")
|
||||||
ErrIdempotencyConflict = errors.New("idempotency key was reused with a different extraction request")
|
ErrIdempotencyConflict = errors.New("idempotency key was reused with a different extraction request")
|
||||||
ErrInvalidCommand = errors.New("invalid extraction command")
|
ErrInvalidCommand = errors.New("invalid extraction command")
|
||||||
|
ErrStoreUnavailable = errors.New("extraction store unavailable")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Candidate struct {
|
type Candidate struct {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user