| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- import { describe, expect, it } from 'vitest'
- import {
- attachDiagnostic,
- buildDebugPayload,
- buildDiagnostic,
- extractErrorDetails,
- formatDiagnosticSummary,
- formatRetrySummary,
- } from '../../scripts/upload-diagnostics.mjs'
- describe('upload diagnostics', () => {
- it('extracts network cause details from fetch failures', () => {
- const error = new TypeError('fetch failed', {
- cause: {
- code: 'ECONNRESET',
- message: 'socket hang up',
- syscall: 'read',
- },
- })
- expect(extractErrorDetails(error)).toMatchObject({
- causeCode: 'ECONNRESET',
- causeMessage: 'socket hang up',
- causeSyscall: 'read',
- errorMessage: 'fetch failed',
- errorName: 'TypeError',
- })
- })
- it('formats upload failures with attempt, file index and cause code', () => {
- const diagnostic = buildDiagnostic({
- attempt: 3,
- error: new TypeError('fetch failed', {
- cause: {
- code: 'ECONNRESET',
- message: 'socket hang up',
- },
- }),
- fileIndex: 5,
- fileName: 'SwitchHosts-v4.3.0.6136-linux-amd64.deb',
- maxAttempts: 3,
- method: 'POST',
- progressSnapshot: {
- currentFileBytes: 123,
- totalFiles: 24,
- totalUploadedBytes: 456,
- },
- retryable: false,
- stage: 'upload-asset',
- target: '/upload',
- })
- expect(formatDiagnosticSummary(diagnostic)).toContain('upload-asset failed')
- expect(formatDiagnosticSummary(diagnostic)).toContain('attempt 3/3')
- expect(formatDiagnosticSummary(diagnostic)).toContain('file 5/24')
- expect(formatDiagnosticSummary(diagnostic)).toContain('cause=ECONNRESET')
- })
- it('formats dns failures with the underlying cause code', () => {
- const diagnostic = buildDiagnostic({
- attempt: 2,
- error: new TypeError('fetch failed', {
- cause: {
- code: 'EAI_AGAIN',
- hostname: 'api.github.com',
- message: 'getaddrinfo EAI_AGAIN api.github.com',
- },
- }),
- maxAttempts: 3,
- method: 'GET',
- retryable: true,
- stage: 'find-release',
- target: '/repos/oldj/SwitchHosts/releases?per_page=100&page=1',
- })
- expect(formatRetrySummary(diagnostic, '1.5s')).toContain('cause=EAI_AGAIN')
- expect(formatDiagnosticSummary(diagnostic)).toContain('message=getaddrinfo EAI_AGAIN api.github.com')
- })
- it('includes http status for api failures', () => {
- const diagnostic = buildDiagnostic({
- attempt: 3,
- error: new Error('GET /repos/... failed: 503 Service Unavailable'),
- httpStatus: 503,
- maxAttempts: 3,
- method: 'GET',
- retryable: false,
- stage: 'find-release',
- target: '/repos/oldj/SwitchHosts/releases?per_page=100&page=1',
- })
- expect(formatDiagnosticSummary(diagnostic)).toContain('status=503')
- expect(formatRetrySummary(diagnostic, '3.0s')).toContain('status=503')
- })
- it('builds debug payloads with stack and raw cause fields', () => {
- const error = new TypeError('fetch failed', {
- cause: {
- code: 'ECONNRESET',
- errno: -54,
- message: 'socket hang up',
- },
- })
- const diagnostic = buildDiagnostic({
- attempt: 3,
- error,
- maxAttempts: 3,
- method: 'POST',
- retryable: false,
- stage: 'upload-asset',
- target: '/upload',
- })
- expect(buildDebugPayload(diagnostic, error)).toMatchObject({
- diagnostic: {
- causeCode: 'ECONNRESET',
- stage: 'upload-asset',
- },
- error: {
- cause: {
- code: 'ECONNRESET',
- errno: -54,
- message: 'socket hang up',
- },
- errorMessage: 'fetch failed',
- },
- })
- })
- it('falls back to the top-level error message when no cause exists', () => {
- const diagnostic = buildDiagnostic({
- attempt: 1,
- error: new Error('plain failure'),
- maxAttempts: 3,
- method: 'DELETE',
- retryable: false,
- stage: 'delete-asset',
- target: '/repos/oldj/SwitchHosts/releases/assets/1',
- })
- expect(formatDiagnosticSummary(diagnostic)).toContain('message=plain failure')
- })
- it('attaches diagnostics to error instances for top-level reporting', () => {
- const error = new Error('plain failure')
- const diagnostic = buildDiagnostic({
- attempt: 1,
- error,
- maxAttempts: 3,
- method: 'DELETE',
- retryable: false,
- stage: 'delete-asset',
- target: '/repos/oldj/SwitchHosts/releases/assets/1',
- })
- const attached = attachDiagnostic(error, diagnostic)
- expect(attached.diagnostic).toEqual(diagnostic)
- })
- })
|