AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Middleware Testing Patterns

skill-proffesor-for-testing-agentic-qe-middleware-testing-patterns · by proffesor-for-testing

Enterprise middleware testing patterns for message routing, transformation, DLQ, protocol mediation, ESB error handling, and EIP patterns. Use when testing middleware layers, message brokers, ESBs, or integration buses.

No reviews yet
0 installs
25 views
0.0% view→install

Install

$ agentstack add skill-proffesor-for-testing-agentic-qe-middleware-testing-patterns

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-proffesor-for-testing-agentic-qe-middleware-testing-patterns)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Middleware Testing Patterns? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Middleware Testing Patterns

When testing enterprise middleware, ESBs, or message-driven systems:

  1. VALIDATE message routing logic (content-based, header-based, recipient list)
  2. TEST message transformations end-to-end (input format -> output format)
  3. VERIFY dead letter queue handling (poison messages, retry exhaustion)
  4. ASSERT message ordering and sequencing with correlation IDs
  5. EXERCISE error handling and compensation patterns
  6. TEST protocol mediation (SOAP-to-REST, sync-to-async)
  7. VALIDATE EIP patterns (splitter, aggregator, content enricher, normalizer)

Quick Pattern Selection:

  • Message routing issues -> Content-based router tests
  • Transformation failures -> Schema-in/schema-out validation
  • Lost messages -> DLQ and retry pattern tests
  • Protocol bridging -> Mediation round-trip tests
  • Complex flows -> Correlation ID tracing tests

Critical Success Factors:

  • Middleware is invisible when it works; test the invisible
  • Always validate both the happy path AND the error channel (DLQ)
  • Correlation IDs are your lifeline for tracing multi-hop messages

Quick Reference Card

When to Use

  • Testing ESB message flows (IBM IIB/ACE, MuleSoft, WSO2)
  • Validating message transformations (XSLT, JSON-to-XML, flat-file parsing)
  • Testing message broker routing (MQ, Kafka, RabbitMQ)
  • Verifying dead letter queue behavior
  • Testing protocol mediation (SOAP/REST bridging)
  • Validating Enterprise Integration Patterns (EIP)

Testing Levels

| Level | Purpose | Dependencies | Speed | |-------|---------|--------------|-------| | Unit Transform | Single mapping correctness | None | Fast | | Route Logic | Routing decision accuracy | Mocked endpoints | Fast | | Integration | End-to-end message flow | Broker + endpoints | Medium | | DLQ/Error | Error handling and recovery | Full middleware stack | Slower |

Critical Test Scenarios

| Scenario | Must Test | Example | |----------|----------|---------| | Routing | Correct destination selection | Order type A -> Queue A, type B -> Queue B | | Transformation | Schema compliance after mapping | XML -> JSON field mapping accuracy | | DLQ | Poison message handling | Malformed XML lands in DLQ, not lost | | Ordering | Sequence preservation | Messages 1-2-3 arrive in order | | Correlation | Multi-hop tracing | Request-reply matched by correlation ID | | Retry | Transient failure recovery | 3 retries with backoff, then DLQ | | Mediation | Protocol bridging fidelity | SOAP request produces correct REST call |

Tools

  • Message Brokers: IBM MQ, RabbitMQ, Apache Kafka, ActiveMQ
  • ESBs: IBM IIB/ACE, MuleSoft, WSO2, Apache Camel
  • Testing: SoapUI, Postman, custom harnesses
  • Virtualization: Mountebank, WireMock, HoverFly
  • Monitoring: Splunk, ELK, Datadog

Agent Coordination

  • qe-middleware-validator: Validate routing rules, transformation accuracy, EIP patterns
  • qe-message-broker-tester: Test broker connectivity, DLQ behavior, message ordering
  • qe-soap-tester: SOAP/WSDL validation, WS-Security, protocol mediation

Message Routing Pattern Testing

Content-Based Router

describe('Content-Based Router - Order Type', () => {
  it('routes standard orders to fulfillment queue', async () => {
    const message = {
      correlationId: uuid(),
      body: { orderType: 'STANDARD', orderId: 'ORD-001', items: [{ sku: 'A1', qty: 2 }] }
    };

    await broker.publish('orders.inbound', message);

    const routed = await broker.consume('orders.fulfillment', { timeout: 5000 });
    expect(routed.body.orderId).toBe('ORD-001');
    expect(routed.correlationId).toBe(message.correlationId);

    const dlq = await broker.tryConsume('orders.dlq', { timeout: 1000 });
    expect(dlq).toBeNull(); // Nothing in DLQ
  });

  it('routes express orders to priority queue', async () => {
    const message = {
      correlationId: uuid(),
      body: { orderType: 'EXPRESS', orderId: 'ORD-002', items: [{ sku: 'B1', qty: 1 }] }
    };

    await broker.publish('orders.inbound', message);

    const routed = await broker.consume('orders.priority', { timeout: 5000 });
    expect(routed.body.orderId).toBe('ORD-002');
  });

  it('sends unrecognized order types to DLQ', async () => {
    const message = {
      correlationId: uuid(),
      body: { orderType: 'UNKNOWN', orderId: 'ORD-003' }
    };

    await broker.publish('orders.inbound', message);

    const dlqMessage = await broker.consume('orders.dlq', { timeout: 5000 });
    expect(dlqMessage.body.orderId).toBe('ORD-003');
    expect(dlqMessage.headers['x-error-reason']).toContain('Unrecognized orderType');
  });
});

Header-Based Router

describe('Header-Based Router - Region', () => {
  it('routes by x-region header to regional queues', async () => {
    const regions = ['US', 'EU', 'APAC'];

    for (const region of regions) {
      const message = {
        headers: { 'x-region': region },
        body: { orderId: `ORD-${region}` }
      };

      await broker.publish('orders.global', message);

      const routed = await broker.consume(`orders.${region.toLowerCase()}`, { timeout: 5000 });
      expect(routed.body.orderId).toBe(`ORD-${region}`);
    }
  });

  it('routes messages without region header to default queue', async () => {
    await broker.publish('orders.global', { body: { orderId: 'ORD-NO-REGION' } });

    const routed = await broker.consume('orders.default', { timeout: 5000 });
    expect(routed.body.orderId).toBe('ORD-NO-REGION');
  });
});

Recipient List

describe('Recipient List - Order Notifications', () => {
  it('fans out order to all registered recipients', async () => {
    const message = {
      body: { orderId: 'ORD-100', total: 250.00 }
    };

    await broker.publish('orders.confirmed', message);

    // All three systems should receive the message
    const [warehouse, billing, crm] = await Promise.all([
      broker.consume('notify.warehouse', { timeout: 5000 }),
      broker.consume('notify.billing', { timeout: 5000 }),
      broker.consume('notify.crm', { timeout: 5000 })
    ]);

    expect(warehouse.body.orderId).toBe('ORD-100');
    expect(billing.body.orderId).toBe('ORD-100');
    expect(crm.body.orderId).toBe('ORD-100');
  });
});

Message Transformation Testing

JSON-to-XML Transformation

describe('JSON to XML Transformation', () => {
  it('transforms order JSON to SAP XML format', async () => {
    const jsonInput = {
      orderId: 'ORD-500',
      customer: { id: 'CUST-01', name: 'Acme Corp' },
      items: [
        { sku: 'MAT-100', quantity: 10, unitPrice: 25.00 }
      ]
    };

    await broker.publish('transform.json-in', { body: jsonInput });

    const xmlOutput = await broker.consume('transform.xml-out', { timeout: 5000 });
    const parsed = parseXml(xmlOutput.body);

    expect(parsed.SalesOrder.OrderNumber).toBe('ORD-500');
    expect(parsed.SalesOrder.SoldToParty.CustomerID).toBe('CUST-01');
    expect(parsed.SalesOrder.Items.Item[0].MaterialNumber).toBe('MAT-100');
    expect(parsed.SalesOrder.Items.Item[0].Quantity).toBe('10');
  });

  it('handles missing optional fields gracefully', async () => {
    const jsonInput = {
      orderId: 'ORD-501',
      customer: { id: 'CUST-02' }, // name is missing
      items: [{ sku: 'MAT-200', quantity: 5 }] // unitPrice missing
    };

    await broker.publish('transform.json-in', { body: jsonInput });

    const xmlOutput = await broker.consume('transform.xml-out', { timeout: 5000 });
    const parsed = parseXml(xmlOutput.body);

    expect(parsed.SalesOrder.SoldToParty.Name).toBe(''); // Default empty
    expect(parsed.SalesOrder.Items.Item[0].UnitPrice).toBe('0.00');
  });

  it('rejects invalid input and sends to DLQ with error details', async () => {
    const invalidInput = { items: [] }; // Missing required orderId and customer

    await broker.publish('transform.json-in', { body: invalidInput });

    const dlq = await broker.consume('transform.dlq', { timeout: 5000 });
    expect(dlq.headers['x-error-reason']).toContain('orderId is required');
  });
});

Flat-File to XML Transformation

describe('Flat-File to XML Transformation', () => {
  it('parses fixed-width flat file into structured XML', async () => {
    // Fixed-width: positions 1-10 = orderId, 11-20 = sku, 21-25 = qty
    const flatFile = 'ORD-001   MAT-10000010\nORD-001   MAT-20000005\n';

    await broker.publish('transform.flatfile-in', { body: flatFile });

    const xmlOutput = await broker.consume('transform.flatfile-xml-out', { timeout: 5000 });
    const parsed = parseXml(xmlOutput.body);

    expect(parsed.Orders.Order).toHaveLength(1);
    expect(parsed.Orders.Order[0].Items.Item).toHaveLength(2);
    expect(parsed.Orders.Order[0].Items.Item[0].Quantity).toBe('10');
  });

  it('handles lines with trailing whitespace correctly', async () => {
    const flatFile = 'ORD-002   MAT-30000001   \n';

    await broker.publish('transform.flatfile-in', { body: flatFile });

    const xmlOutput = await broker.consume('transform.flatfile-xml-out', { timeout: 5000 });
    const parsed = parseXml(xmlOutput.body);

    expect(parsed.Orders.Order[0].OrderId).toBe('ORD-002');
    expect(parsed.Orders.Order[0].Items.Item[0].Quantity).toBe('1');
  });
});

Dead Letter Queue (DLQ) Testing

describe('Dead Letter Queue Strategy', () => {
  it('moves message to DLQ after max retries', async () => {
    // Configure endpoint to always fail
    mockEndpoint.respondWith(500, 'Service Unavailable');

    await broker.publish('orders.process', {
      body: { orderId: 'ORD-FAIL' },
      headers: { 'x-correlation-id': 'corr-123' }
    });

    // Wait for retries to exhaust (3 retries with backoff)
    const dlqMessage = await broker.consume('orders.dlq', { timeout: 30000 });

    expect(dlqMessage.body.orderId).toBe('ORD-FAIL');
    expect(dlqMessage.headers['x-retry-count']).toBe('3');
    expect(dlqMessage.headers['x-original-queue']).toBe('orders.process');
    expect(dlqMessage.headers['x-correlation-id']).toBe('corr-123');
    expect(dlqMessage.headers['x-error-reason']).toContain('Service Unavailable');
    expect(dlqMessage.headers['x-first-failure-timestamp']).toBeDefined();
  });

  it('preserves original message body in DLQ', async () => {
    const originalBody = {
      orderId: 'ORD-PRESERVE',
      items: [{ sku: 'A', qty: 1 }, { sku: 'B', qty: 2 }]
    };

    mockEndpoint.respondWith(500, 'Timeout');
    await broker.publish('orders.process', { body: originalBody });

    const dlqMessage = await broker.consume('orders.dlq', { timeout: 30000 });
    expect(dlqMessage.body).toEqual(originalBody); // Body unchanged
  });

  it('DLQ messages can be replayed after fix', async () => {
    // Step 1: Message fails, lands in DLQ
    mockEndpoint.respondWith(500, 'Down');
    await broker.publish('orders.process', { body: { orderId: 'ORD-REPLAY' } });
    const dlqMessage = await broker.consume('orders.dlq', { timeout: 30000 });

    // Step 2: Fix the endpoint
    mockEndpoint.respondWith(200, 'OK');

    // Step 3: Replay from DLQ
    await broker.publish('orders.process', dlqMessage);
    const success = await broker.consume('orders.completed', { timeout: 5000 });
    expect(success.body.orderId).toBe('ORD-REPLAY');
  });
});

Message Ordering and Sequencing

describe('Message Ordering Validation', () => {
  it('preserves FIFO order within same partition key', async () => {
    const messages = Array.from({ length: 10 }, (_, i) => ({
      headers: { 'x-partition-key': 'ORDER-GROUP-1', 'x-sequence': String(i + 1) },
      body: { orderId: `ORD-${i + 1}`, sequence: i + 1 }
    }));

    for (const msg of messages) {
      await broker.publish('orders.sequenced', msg);
    }

    const received = [];
    for (let i = 0; i  {
    // Send messages 1, 2, 4 (skip 3)
    for (const seq of [1, 2, 4]) {
      await broker.publish('orders.sequenced', {
        headers: { 'x-partition-key': 'GROUP-2', 'x-sequence': String(seq) },
        body: { sequence: seq }
      });
    }

    // The middleware should detect the gap and raise an alert
    const alert = await broker.consume('orders.sequence-gap', { timeout: 10000 });
    expect(alert.body.missingSequence).toBe(3);
    expect(alert.body.partitionKey).toBe('GROUP-2');
  });
});

ESB Error Handling and Compensation

describe('Compensation Pattern - Saga', () => {
  it('compensates completed steps when a later step fails', async () => {
    // Saga: Reserve Inventory -> Charge Payment -> Ship
    // If Ship fails, Payment and Inventory must be compensated

    mockServices.inventory.respondWith(200); // Step 1 succeeds
    mockServices.payment.respondWith(200);   // Step 2 succeeds
    mockServices.shipping.respondWith(500);  // Step 3 fails

    await broker.publish('saga.order', {
      body: { orderId: 'ORD-SAGA', amount: 100.00 }
    });

    // Verify compensation messages were sent
    const paymentRefund = await broker.consume('saga.compensate.payment', { timeout: 10000 });
    expect(paymentRefund.body.action).toBe('REFUND');
    expect(paymentRefund.body.orderId).toBe('ORD-SAGA');

    const inventoryRelease = await broker.consume('saga.compensate.inventory', { timeout: 10000 });
    expect(inventoryRelease.body.action).toBe('RELEASE');
    expect(inventoryRelease.body.orderId).toBe('ORD-SAGA');
  });
});

Protocol Mediation Testing

SOAP-to-REST Mediation

describe('SOAP to REST Protocol Mediation', () => {
  it('converts SOAP request to REST call and back', async () => {
    const soapRequest = `
      
        
          
            ORD-700
          
        
      
    `;

    // ESB should convert SOAP -> REST GET /orders/ORD-700
    mockRestBackend.onGet('/orders/ORD-700').reply(200, {
      orderId: 'ORD-700',
      status: 'SHIPPED',
      total: 150.00
    });

    const response = await soapClient.send(soapRequest, { endpoint: esbSoapEndpoint });

    // Verify SOAP response wraps REST data correctly
    const parsed = parseSoapResponse(response);
    expect(parsed.OrderId).toBe('ORD-700');
    expect(parsed.Status).toBe('SHIPPED');
    expect(parsed.Total).toBe('150.00');
  });

  it('maps REST error to SOAP fault', async () => {
    const soapRequest = buildSoapGetOrder('ORD-NOTFOUND');

    mockRestBackend.onGet('/orders/ORD-NOTFOUND').reply(404, {
      error: 'Order not found'
    });

    const response = await soapClient.send(soapRequest, { endpoint: esbSoapEndpoint });
    const fault = parseSoapFault(response);

    expect(fault.faultCode).toBe('Client');
    expect(fault.faultString).toContain('Order not found');
  });
});

Sync-to-Async Mediation

describe('Sync to Async Mediation', () => {
  it('accepts sync request, returns correlation ID, delivers result async', async () => {
    // Client sends synchronous HTTP POST
    const response = await httpClient.post(`${esbEndpoint}/async-orders`, {
      orderId: 'ORD-ASYNC',
      items: [{ sku: 'X1', qty: 5 }]
    });

    // ESB returns 202 Accepted with correlation ID
    expect(response.status).toBe(202);
    expect(response.body.correlationId).toBeDefined();
    const correlationId = response.body.correlationId;

    // Result arrives asynchronously on callback queue
    const result = await broker.consume('orders.async.results', {
      timeout: 15000,
      filter: (msg) => msg.headers['x-correlation-id'] === correlationId
    });

    expect(result.body.orderId).toBe('ORD-ASYNC');
    expect(result.body.status).toBe('ACCEPTED');
  });
});

Enterprise Integration Patterns (EIP)

Splitter Pattern

describe('Splitter - Batch Order to Individual Items', () => {
  it('splits batch order into individual item messages', async () => {
    const batchOrder = {
      orderId: 'BATCH-001',
      items: [
        { sku: 'A1', qty: 10 },

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [proffesor-for-testing](https://github.com/proffesor-for-testing)
- **Source:** [proffesor-for-testing/agentic-qe](https://github.com/proffesor-for-testing/agentic-qe)
- **License:** MIT
- **Homepage:** https://agentic-qe.dev/

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.