chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { ErrorExtractorId, type ErrorInfo, extractErrorMessage } from '@/tools/error-extractors'
|
||||
|
||||
describe('Error Extractors', () => {
|
||||
describe('extractErrorMessage', () => {
|
||||
it('should extract GraphQL error messages', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 400,
|
||||
data: {
|
||||
errors: [{ message: 'GraphQL validation error' }],
|
||||
},
|
||||
}
|
||||
expect(extractErrorMessage(errorInfo)).toBe('GraphQL validation error')
|
||||
})
|
||||
|
||||
it('should extract Twitter API error details', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 403,
|
||||
data: {
|
||||
errors: [{ detail: 'Rate limit exceeded' }],
|
||||
},
|
||||
}
|
||||
expect(extractErrorMessage(errorInfo)).toBe('Rate limit exceeded')
|
||||
})
|
||||
|
||||
it('should extract Telegram API description', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 403,
|
||||
data: {
|
||||
ok: false,
|
||||
error_code: 403,
|
||||
description: "Forbidden: bots can't send messages to bots",
|
||||
},
|
||||
}
|
||||
expect(extractErrorMessage(errorInfo)).toBe("Forbidden: bots can't send messages to bots")
|
||||
})
|
||||
|
||||
it('should extract standard message field', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 400,
|
||||
data: {
|
||||
message: 'Invalid request parameters',
|
||||
},
|
||||
}
|
||||
expect(extractErrorMessage(errorInfo)).toBe('Invalid request parameters')
|
||||
})
|
||||
|
||||
it('should extract OAuth error_description', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 401,
|
||||
data: {
|
||||
error: 'invalid_grant',
|
||||
error_description: 'The provided authorization grant is invalid',
|
||||
},
|
||||
}
|
||||
expect(extractErrorMessage(errorInfo)).toBe('The provided authorization grant is invalid')
|
||||
})
|
||||
|
||||
it('should extract SOAP fault strings', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 500,
|
||||
data: {
|
||||
fault: {
|
||||
faultstring: 'SOAP processing error',
|
||||
},
|
||||
},
|
||||
}
|
||||
expect(extractErrorMessage(errorInfo)).toBe('SOAP processing error')
|
||||
})
|
||||
|
||||
it('should extract nested error object messages', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 400,
|
||||
data: {
|
||||
error: {
|
||||
message: 'Resource not found',
|
||||
},
|
||||
},
|
||||
}
|
||||
expect(extractErrorMessage(errorInfo)).toBe('Resource not found')
|
||||
})
|
||||
|
||||
it('should handle string error field', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 400,
|
||||
data: {
|
||||
error: 'Bad request',
|
||||
},
|
||||
}
|
||||
expect(extractErrorMessage(errorInfo)).toBe('Bad request')
|
||||
})
|
||||
|
||||
it('should extract errors array with strings', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 400,
|
||||
data: {
|
||||
errors: ['Email is required', 'Password is too short'],
|
||||
},
|
||||
}
|
||||
expect(extractErrorMessage(errorInfo)).toBe('Email is required')
|
||||
})
|
||||
|
||||
it('should extract plain text error responses', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 401,
|
||||
statusText: 'Unauthorized',
|
||||
data: 'Invalid access token',
|
||||
}
|
||||
expect(extractErrorMessage(errorInfo)).toBe('Invalid access token')
|
||||
})
|
||||
|
||||
it('should extract plain text error from APIs like Apollo', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 403,
|
||||
statusText: 'Forbidden',
|
||||
data: 'Invalid API key provided',
|
||||
}
|
||||
expect(extractErrorMessage(errorInfo)).toBe('Invalid API key provided')
|
||||
})
|
||||
|
||||
it('should trim whitespace from plain text errors', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 400,
|
||||
statusText: 'Bad Request',
|
||||
data: ' Error message with spaces ',
|
||||
}
|
||||
expect(extractErrorMessage(errorInfo)).toBe('Error message with spaces')
|
||||
})
|
||||
|
||||
it('should skip empty plain text and use HTTP status text fallback', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 500,
|
||||
statusText: 'Internal Server Error',
|
||||
data: ' ',
|
||||
}
|
||||
expect(extractErrorMessage(errorInfo)).toBe('Internal Server Error')
|
||||
})
|
||||
|
||||
it('should use HTTP status text as fallback', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 404,
|
||||
statusText: 'Not Found',
|
||||
data: {},
|
||||
}
|
||||
expect(extractErrorMessage(errorInfo)).toBe('Not Found')
|
||||
})
|
||||
|
||||
it('should use final fallback when no pattern matches', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 500,
|
||||
data: {},
|
||||
}
|
||||
expect(extractErrorMessage(errorInfo)).toBe('Request failed with status 500')
|
||||
})
|
||||
|
||||
it('should handle undefined errorInfo', () => {
|
||||
expect(extractErrorMessage(undefined)).toBe('Request failed with status unknown')
|
||||
})
|
||||
|
||||
it('should handle empty strings gracefully', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 400,
|
||||
data: {
|
||||
message: '',
|
||||
},
|
||||
}
|
||||
expect(extractErrorMessage(errorInfo)).toBe('Request failed with status 400')
|
||||
})
|
||||
})
|
||||
|
||||
describe('extractErrorMessage with explicit extractorId', () => {
|
||||
it('should use specified extractor directly (deterministic)', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 403,
|
||||
data: {
|
||||
description: "Forbidden: bots can't send messages to bots",
|
||||
message: 'Some other message',
|
||||
},
|
||||
}
|
||||
|
||||
expect(extractErrorMessage(errorInfo, ErrorExtractorId.TELEGRAM_DESCRIPTION)).toBe(
|
||||
"Forbidden: bots can't send messages to bots"
|
||||
)
|
||||
})
|
||||
|
||||
it('should use specified extractor even when other patterns match first', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 400,
|
||||
data: {
|
||||
errors: [{ message: 'GraphQL error' }],
|
||||
message: 'Standard message',
|
||||
},
|
||||
}
|
||||
|
||||
expect(extractErrorMessage(errorInfo, ErrorExtractorId.STANDARD_MESSAGE)).toBe(
|
||||
'Standard message'
|
||||
)
|
||||
})
|
||||
|
||||
it('should fallback when specified extractor does not find message', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 404,
|
||||
data: {
|
||||
someOtherField: 'value',
|
||||
},
|
||||
}
|
||||
|
||||
expect(extractErrorMessage(errorInfo, ErrorExtractorId.TELEGRAM_DESCRIPTION)).toBe(
|
||||
'Request failed with status 404'
|
||||
)
|
||||
})
|
||||
|
||||
it('should warn and fallback for non-existent extractor ID', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 500,
|
||||
data: {
|
||||
message: 'Error message',
|
||||
},
|
||||
}
|
||||
|
||||
expect(extractErrorMessage(errorInfo, 'non-existent-extractor')).toBe(
|
||||
'Request failed with status 500'
|
||||
)
|
||||
})
|
||||
|
||||
it('should use plain text extractor when explicitly specified', () => {
|
||||
const errorInfo: ErrorInfo = {
|
||||
status: 403,
|
||||
statusText: 'Forbidden',
|
||||
data: 'Plain text error message',
|
||||
}
|
||||
|
||||
expect(extractErrorMessage(errorInfo, ErrorExtractorId.PLAIN_TEXT_DATA)).toBe(
|
||||
'Plain text error message'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user