Skip to content

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

POST /api/connectors/:id/test

Response

{
  "success": true,
  "message": "OK",
  "durationMs": 245,
  "data": { "version": "2.1.0", "status": "healthy" }
}

The test command:

  1. Retrieves the connector definition from the database
  2. Decrypts the connection configuration
  3. Executes the test command through the connector's registered handler
  4. 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:

  1. Static mock data for the command and scenario
  2. Mock script if defined
  3. 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:

PUT /api/connectors/:id
{ "debugLogging": true }

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:

PUT /api/connectors/:id
{ "logLevel": "debug" }

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:

CONNECTOR_DEBUG=my-crm node packages/server/dist/index.js

Viewing Execution Logs

  1. Open the connector's detail page
  2. Navigate to the Logs tab
  3. Filter by level, time range, or search term
  4. Each log entry includes:
  5. Timestamp
  6. Log level
  7. Message
  8. 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:

  • [ ] test command succeeds with valid credentials
  • [ ] test command 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