Testing Connectors¶
This guide covers strategies for testing connectors during development and in production-like environments.
Test Command¶
Every connector should implement a test command that verifies basic connectivity without side effects.
Via API¶
Response¶
{
"success": true,
"message": "OK",
"durationMs": 245,
"data": { "version": "2.1.0", "status": "healthy" }
}
The test command:
- Retrieves the connector definition from the database
- Decrypts the connection configuration
- Executes the
testcommand through the connector's registered handler - Returns the result with timing information
Writing a Good Test Command¶
function test() {
var resp = floh.http.get(floh.config.baseUrl + '/api/health', {
'Authorization': 'Bearer ' + floh.config.apiKey
});
if (resp.status >= 400) {
return {
success: false,
error: 'Health check failed with status ' + resp.status
};
}
return {
success: true,
data: { version: resp.body.version, latency: resp.body.latency }
};
}
Executing Commands¶
Test individual commands with specific parameters:
POST /api/connectors/:id/execute
Content-Type: application/json
{
"command": "listContacts",
"limit": 5,
"offset": 0
}
Response¶
{
"success": true,
"data": {
"contacts": [
{ "id": "c1", "name": "Alice" },
{ "id": "c2", "name": "Bob" }
],
"total": 42
},
"outputVariables": {
"contacts": [...],
"total": 42
},
"durationMs": 312
}
Mock Mode¶
Mock mode lets you test workflows and connectors without connecting to real external services. Mocks operate at four levels, with higher-priority levels overriding lower ones.
Activation Levels¶
| Level | How to Activate | Priority |
|---|---|---|
| Step | Set mock: true in the workflow step config |
Highest |
| Run | Set useMocks: true when starting a workflow run |
High |
| Connector | Set mockEnabled: true on the connector definition |
Medium |
| System | Set CONNECTOR_MOCK_MODE=true environment variable |
Lowest |
If any level is active, mock mode is used.
Static Mock Data¶
Define mock responses for each command with named scenarios:
PUT /api/connectors/:id
Content-Type: application/json
{
"mockData": {
"listContacts": {
"default": {
"success": true,
"data": { "contacts": [{ "id": "c1", "name": "Test User" }], "total": 1 },
"outputVariables": { "total": 1 }
},
"empty": {
"success": true,
"data": { "contacts": [], "total": 0 },
"outputVariables": { "total": 0 }
},
"error": {
"success": false,
"error": "Connection refused"
}
},
"createContact": {
"default": {
"success": true,
"data": { "contactId": "mock-c-1" }
}
}
},
"mockEnabled": true
}
Selecting Mock Scenarios¶
In a workflow step, specify which scenario to use:
{
"type": "connector",
"config": {
"connector": "my-crm",
"command": "listContacts",
"mock": true,
"mockScenario": "empty"
}
}
If the specified scenario doesn't exist, the default scenario is used as a fallback.
Dynamic Mock Scripts¶
For more complex mock behavior, write a mock script that runs in the same QuickJS sandbox as regular connector scripts:
PUT /api/connectors/:id
Content-Type: application/json
{
"mockScript": "function listContacts() {\n var count = floh.params.limit || 10;\n var contacts = [];\n for (var i = 0; i < count; i++) {\n contacts.push({ id: 'mock-' + i, name: 'User ' + i });\n }\n return { success: true, data: { contacts: contacts, total: count } };\n}"
}
Mock scripts receive the same floh.* context as regular scripts, letting you generate realistic responses based on input parameters.
Auto-Generated Mocks¶
When no static mock data or mock script is defined, Floh generates mock responses from the connector's configSchema:
- Output fields are populated with plausible mock values based on field names
- Fields containing "email" →
mock@example.com - Fields containing "id" →
mock_fieldName_1 - Fields containing "count" or "total" →
1 - Fields containing "users", "members", etc. →
[{ id: "mock_1", name: "Mock Item" }] - Boolean-like fields (
isMember,exists,success) →true
Auto-generated mocks include mock: true in the data to make them identifiable.
Mock Execution Priority¶
When mock mode is active, the engine tries sources in order:
- Static mock data for the command and scenario
- Mock script if defined
- Auto-generated mock from
configSchema
Unit Testing Connectors¶
Testing Script Connectors¶
Create unit tests that validate your connector's JavaScript functions independently:
import { describe, it, expect } from 'vitest';
import { executeInSandbox } from '../../src/modules/connectors/script-runtime.js';
describe('My CRM Connector', () => {
const scriptSource = `
function listContacts() {
var resp = floh.http.get(floh.config.baseUrl + '/contacts');
if (resp.status >= 400) return { success: false, error: 'Failed' };
return { success: true, data: resp.body };
}
function test() {
return { success: true };
}
`;
it('executes the test command', async () => {
const result = await executeInSandbox(scriptSource, 'test', {
config: { command: 'test' },
connectorConfig: { baseUrl: 'https://api.example.com' },
variables: {},
stepId: 's1',
runId: 'r1',
}, 5000);
expect(result.success).toBe(true);
});
});
Testing Schema Versioning¶
import { compareSchemas } from '../../src/modules/connectors/schema-versioning.js';
const oldSchema = { commands: { list: { params: ['limit'], ... } } };
const newSchema = { commands: { list: { params: ['limit', 'filter'], ... } } };
const result = compareSchemas(oldSchema, newSchema);
expect(result.hasBreakingChanges).toBe(true);
Integration Testing¶
Testing via the API¶
Use supertest or any HTTP client to test the full connector lifecycle:
import request from 'supertest';
it('creates and tests a connector', async () => {
const created = await request(app.server)
.post('/api/connectors')
.send({ name: 'test-conn', type: 'http', version: '1.0.0', configSchema: {} })
.expect(201);
const testResult = await request(app.server)
.post(`/api/connectors/${created.body.id}/test`)
.expect(200);
expect(testResult.body.success).toBeDefined();
});
Testing with Mock Mode¶
Integration tests can use mock mode to avoid external dependencies:
it('executes workflow with mocked connector', async () => {
const connector = await createConnector({
mockEnabled: true,
mockData: {
doThing: {
default: { success: true, data: { done: true } }
}
}
});
// Workflow steps using this connector will receive mock responses
});
Debugging¶
Enable Debug Logging¶
The quickest way to enable verbose logging for a single connector is the debugLogging flag:
This overrides the connector's logLevel to debug, writing all execution events (call start/end, HTTP requests/responses, timeouts) to the system_log table. You can also lower the logLevel directly:
For global debug logging across all connectors, set DEBUG_LOGGING=true in the server environment.
For lightweight console-only debugging (not persisted), use the CONNECTOR_DEBUG environment variable:
Viewing Execution Logs¶
- Open the connector's detail page
- Navigate to the Logs tab
- Filter by level, time range, or search term
- Each log entry includes:
- Timestamp
- Log level
- Message
- Metadata (connector name, command, duration, run ID)
Common Issues¶
| Symptom | Likely Cause | Solution |
|---|---|---|
Test returns success: false |
Wrong connection config | Verify base URL and credentials |
| Script timeout | Infinite loop or slow API | Check script logic, increase timeout |
| "No script source defined" | Missing scriptSource field |
Upload the script to the connector |
| "No endpoint URL configured" | External connector without URL | Set endpointUrl |
| "No agent connected" | Agent WebSocket disconnected | Check agent status and network |
| Mock returns unexpected data | Wrong scenario selected | Verify mockScenario in step config |
| "Connector is disabled" | enabled: false |
Re-enable the connector |
Test Coverage Checklist¶
When building a connector, verify:
- [ ]
testcommand succeeds with valid credentials - [ ]
testcommand fails gracefully with invalid credentials - [ ] Each command returns expected data format
- [ ] Error responses include meaningful error messages
- [ ] Mock data covers happy path and error scenarios
- [ ] Schema comparison detects expected changes
- [ ] Impact analysis correctly identifies dependent workflows
- [ ] Logs appear at the correct level
- [ ] Secrets are not exposed in logs or error messages