mirror of
https://github.com/xtr-dev/payload-automation.git
synced 2025-12-10 17:03:22 +00:00
Major Features: • Add persistent error tracking for timeout/network failures that bypasses PayloadCMS output limitations • Implement smart error classification (timeout, DNS, connection, network) with duration-based detection • Add comprehensive test infrastructure with MongoDB in-memory testing and enhanced mocking • Fix HTTP request handler error preservation with detailed context storage • Add independent execution tracking with success/failure status and duration metrics Technical Improvements: • Update JSONPath documentation to use correct $.trigger.doc syntax across all step types • Fix PayloadCMS job execution to use runByID instead of run() for reliable task processing • Add enhanced HTTP error handling that preserves outputs for 4xx/5xx status codes • Implement proper nock configuration with undici for Node.js 22 fetch interception • Add comprehensive unit tests for WorkflowExecutor with mocked PayloadCMS instances Developer Experience: • Add detailed error information in workflow context with URL, method, timeout, attempts • Update README with HTTP error handling patterns and enhanced error tracking examples • Add test helpers and setup infrastructure for reliable integration testing • Fix workflow step validation and JSONPath field descriptions Breaking Changes: None - fully backward compatible 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
201 lines
4.8 KiB
TypeScript
201 lines
4.8 KiB
TypeScript
import nock from 'nock'
|
|
|
|
/**
|
|
* Mock HTTP requests to httpbin.org for testing
|
|
*/
|
|
export const mockHttpBin = {
|
|
/**
|
|
* Mock a successful POST request to httpbin.org/post
|
|
*/
|
|
mockPost: (expectedData?: any) => {
|
|
return nock('https://httpbin.org')
|
|
.post('/post')
|
|
.reply(200, {
|
|
args: {},
|
|
data: JSON.stringify(expectedData || {}),
|
|
files: {},
|
|
form: {},
|
|
headers: {
|
|
'Accept': '*/*',
|
|
'Accept-Encoding': 'br, gzip, deflate',
|
|
'Accept-Language': '*',
|
|
'Content-Type': 'application/json',
|
|
'Host': 'httpbin.org',
|
|
'Sec-Fetch-Mode': 'cors',
|
|
'User-Agent': 'PayloadCMS-Automation/1.0'
|
|
},
|
|
json: expectedData || {},
|
|
origin: '127.0.0.1',
|
|
url: 'https://httpbin.org/post'
|
|
}, {
|
|
'Content-Type': 'application/json',
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Access-Control-Allow-Credentials': 'true'
|
|
})
|
|
},
|
|
|
|
/**
|
|
* Mock a GET request to httpbin.org/get
|
|
*/
|
|
mockGet: () => {
|
|
return nock('https://httpbin.org')
|
|
.get('/get')
|
|
.reply(200, {
|
|
args: {},
|
|
headers: {
|
|
'Accept': '*/*',
|
|
'Host': 'httpbin.org',
|
|
'User-Agent': 'PayloadCMS-Automation/1.0'
|
|
},
|
|
origin: '127.0.0.1',
|
|
url: 'https://httpbin.org/get'
|
|
})
|
|
},
|
|
|
|
/**
|
|
* Mock HTTP timeout
|
|
*/
|
|
mockTimeout: (path: string = '/delay/10') => {
|
|
return nock('https://httpbin.org')
|
|
.get(path)
|
|
.replyWithError({
|
|
code: 'ECONNABORTED',
|
|
message: 'timeout of 2000ms exceeded'
|
|
})
|
|
},
|
|
|
|
/**
|
|
* Mock HTTP error responses
|
|
*/
|
|
mockError: (status: number, path: string = '/status/' + status) => {
|
|
return nock('https://httpbin.org')
|
|
.get(path)
|
|
.reply(status, {
|
|
error: `HTTP ${status} Error`,
|
|
message: `Mock ${status} response`
|
|
})
|
|
},
|
|
|
|
/**
|
|
* Mock invalid URL to simulate network errors
|
|
*/
|
|
mockNetworkError: (url: string = 'invalid-url-that-will-fail') => {
|
|
return nock('https://' + url)
|
|
.get('/')
|
|
.replyWithError({
|
|
code: 'ENOTFOUND',
|
|
message: `getaddrinfo ENOTFOUND ${url}`
|
|
})
|
|
},
|
|
|
|
/**
|
|
* Mock HTML response (non-JSON)
|
|
*/
|
|
mockHtml: () => {
|
|
return nock('https://httpbin.org')
|
|
.get('/html')
|
|
.reply(200, '<!DOCTYPE html><html><head><title>Test</title></head><body>Test HTML</body></html>', {
|
|
'Content-Type': 'text/html'
|
|
})
|
|
},
|
|
|
|
/**
|
|
* Mock all common endpoints for error scenarios
|
|
*/
|
|
mockAllErrorScenarios: () => {
|
|
// HTML response for invalid JSON test
|
|
nock('https://httpbin.org')
|
|
.get('/html')
|
|
.reply(200, '<!DOCTYPE html><html><head><title>Test</title></head><body>Test HTML</body></html>', {
|
|
'Content-Type': 'text/html'
|
|
})
|
|
|
|
// 404 error
|
|
nock('https://httpbin.org')
|
|
.get('/status/404')
|
|
.reply(404, {
|
|
error: 'Not Found',
|
|
message: 'The requested resource was not found'
|
|
})
|
|
|
|
// 500 error
|
|
nock('https://httpbin.org')
|
|
.get('/status/500')
|
|
.reply(500, {
|
|
error: 'Internal Server Error',
|
|
message: 'Server encountered an error'
|
|
})
|
|
|
|
// 503 error for retry tests
|
|
nock('https://httpbin.org')
|
|
.get('/status/503')
|
|
.times(3) // Allow 3 retries
|
|
.reply(503, {
|
|
error: 'Service Unavailable',
|
|
message: 'Service is temporarily unavailable'
|
|
})
|
|
|
|
// POST endpoint for circular reference and other POST tests
|
|
nock('https://httpbin.org')
|
|
.post('/post')
|
|
.times(5) // Allow multiple POST requests
|
|
.reply(200, (uri, requestBody) => ({
|
|
args: {},
|
|
data: JSON.stringify(requestBody),
|
|
json: requestBody,
|
|
url: 'https://httpbin.org/post'
|
|
}))
|
|
},
|
|
|
|
/**
|
|
* Clean up all nock mocks
|
|
*/
|
|
cleanup: () => {
|
|
nock.cleanAll()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test fixtures for common workflow configurations
|
|
*/
|
|
export const testFixtures = {
|
|
basicWorkflow: {
|
|
name: 'Test Basic Workflow',
|
|
description: 'Basic workflow for testing',
|
|
triggers: [
|
|
{
|
|
type: 'collection-trigger' as const,
|
|
collectionSlug: 'posts',
|
|
operation: 'create' as const
|
|
}
|
|
]
|
|
},
|
|
|
|
httpRequestStep: (url: string = 'https://httpbin.org/post', expectedData?: any) => ({
|
|
name: 'http-request',
|
|
step: 'http-request-step',
|
|
url,
|
|
method: 'POST' as const,
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: expectedData || {
|
|
message: 'Test request',
|
|
data: '$.trigger.doc'
|
|
}
|
|
}),
|
|
|
|
createDocumentStep: (collectionSlug: string = 'auditLog') => ({
|
|
name: 'create-audit',
|
|
step: 'create-document',
|
|
collectionSlug,
|
|
data: {
|
|
message: 'Test document created',
|
|
sourceId: '$.trigger.doc.id'
|
|
}
|
|
}),
|
|
|
|
testPost: {
|
|
content: 'Test post content for workflow trigger'
|
|
}
|
|
} |