d25d482dc2
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
239 lines
6.9 KiB
TypeScript
239 lines
6.9 KiB
TypeScript
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'
|
|
)
|
|
})
|
|
})
|
|
})
|