| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- package service
- import (
- "context"
- "errors"
- "testing"
- "time"
- "github.com/stretchr/testify/require"
- )
- func TestOpsServiceRecordErrorBatch_SanitizesAndBatches(t *testing.T) {
- t.Parallel()
- var captured []*OpsInsertErrorLogInput
- repo := &opsRepoMock{
- BatchInsertErrorLogsFn: func(ctx context.Context, inputs []*OpsInsertErrorLogInput) (int64, error) {
- captured = append(captured, inputs...)
- return int64(len(inputs)), nil
- },
- }
- svc := NewOpsService(repo, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
- msg := " upstream failed: https://example.com?access_token=secret-value "
- detail := `{"authorization":"Bearer secret-token"}`
- entries := []*OpsInsertErrorLogInput{
- {
- ErrorBody: `{"error":"bad","access_token":"secret"}`,
- UpstreamStatusCode: intPtr(-10),
- UpstreamErrorMessage: strPtr(msg),
- UpstreamErrorDetail: strPtr(detail),
- UpstreamErrors: []*OpsUpstreamErrorEvent{
- {
- AccountID: -2,
- UpstreamStatusCode: 429,
- Message: " token leaked ",
- Detail: `{"refresh_token":"secret"}`,
- },
- },
- },
- {
- ErrorPhase: "upstream",
- ErrorType: "upstream_error",
- CreatedAt: time.Now().UTC(),
- },
- }
- require.NoError(t, svc.RecordErrorBatch(context.Background(), entries))
- require.Len(t, captured, 2)
- first := captured[0]
- require.Equal(t, "internal", first.ErrorPhase)
- require.Equal(t, "api_error", first.ErrorType)
- require.Nil(t, first.UpstreamStatusCode)
- require.NotNil(t, first.UpstreamErrorMessage)
- require.NotContains(t, *first.UpstreamErrorMessage, "secret-value")
- require.Contains(t, *first.UpstreamErrorMessage, "access_token=***")
- require.NotNil(t, first.UpstreamErrorDetail)
- require.NotContains(t, *first.UpstreamErrorDetail, "secret-token")
- require.NotContains(t, first.ErrorBody, "secret")
- require.Nil(t, first.UpstreamErrors)
- require.NotNil(t, first.UpstreamErrorsJSON)
- require.NotContains(t, *first.UpstreamErrorsJSON, "secret")
- require.Contains(t, *first.UpstreamErrorsJSON, "[REDACTED]")
- second := captured[1]
- require.Equal(t, "upstream", second.ErrorPhase)
- require.Equal(t, "upstream_error", second.ErrorType)
- require.False(t, second.CreatedAt.IsZero())
- }
- func TestOpsServiceRecordErrorBatch_DoesNotFallbackToSingleInsertsWhenBatchFails(t *testing.T) {
- t.Parallel()
- var (
- batchCalls int
- singleCalls int
- )
- repo := &opsRepoMock{
- BatchInsertErrorLogsFn: func(ctx context.Context, inputs []*OpsInsertErrorLogInput) (int64, error) {
- batchCalls++
- return 0, errors.New("batch failed")
- },
- InsertErrorLogFn: func(ctx context.Context, input *OpsInsertErrorLogInput) (int64, error) {
- singleCalls++
- return int64(singleCalls), nil
- },
- }
- svc := NewOpsService(repo, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
- err := svc.RecordErrorBatch(context.Background(), []*OpsInsertErrorLogInput{
- {ErrorMessage: "first"},
- {ErrorMessage: "second"},
- })
- require.Error(t, err)
- require.Equal(t, 1, batchCalls)
- require.Zero(t, singleCalls)
- }
- func TestOpsServiceRecordErrorPersistsExplicitAccountAuthStatusZero(t *testing.T) {
- t.Parallel()
- var captured *OpsInsertErrorLogInput
- repo := &opsRepoMock{
- InsertErrorLogFn: func(_ context.Context, input *OpsInsertErrorLogInput) (int64, error) {
- captured = input
- return 1, nil
- },
- }
- svc := NewOpsService(repo, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
- staleStatus := 403
- staleMessage := "stale inference message"
- staleDetail := "stale inference detail"
- err := svc.RecordError(context.Background(), &OpsInsertErrorLogInput{
- ErrorPhase: "upstream",
- ErrorType: "upstream_error",
- ErrorOwner: "provider",
- ErrorSource: "upstream_http",
- UpstreamStatusCode: &staleStatus,
- UpstreamErrorMessage: &staleMessage,
- UpstreamErrorDetail: &staleDetail,
- UpstreamErrors: []*OpsUpstreamErrorEvent{
- {Stage: string(GatewayFailureStageInference), UpstreamStatusCode: 403, Message: staleMessage, Detail: staleDetail},
- {
- Stage: string(GatewayFailureStageAccountAuth), Scope: string(GatewayFailureScopeAccount),
- Reason: string(GrokCredentialReasonRevoked), Message: "Grok OAuth credentials require account action",
- },
- },
- })
- require.NoError(t, err)
- require.NotNil(t, captured)
- require.Equal(t, "account_auth", captured.ErrorPhase)
- require.Equal(t, "provider", captured.ErrorOwner)
- require.Equal(t, "gateway", captured.ErrorSource)
- require.NotNil(t, captured.UpstreamStatusCode)
- require.Zero(t, *captured.UpstreamStatusCode)
- require.NotNil(t, captured.UpstreamErrorMessage)
- require.Equal(t, "Grok OAuth credentials require account action", *captured.UpstreamErrorMessage)
- require.Nil(t, captured.UpstreamErrorDetail)
- require.Nil(t, captured.UpstreamErrors)
- require.NotNil(t, captured.UpstreamErrorsJSON)
- require.Contains(t, *captured.UpstreamErrorsJSON, `"upstream_status_code":403`)
- require.Contains(t, *captured.UpstreamErrorsJSON, `"stage":"account_auth"`)
- }
- func strPtr(v string) *string {
- return &v
- }
|