mcpField
Q
qe-middleware-testing-patterns

qe-middleware-testing-patterns

github.com/proffesor-for-testing测试与质量51980 次安装今天更新
代码仓库

关于 qe-middleware-testing-patterns

面向企业中间件、ESB 和消息驱动系统的测试模式 Skill,指导消息路由、转换、错误处理、排序、协议中介及集成模式测试,也涵盖 Agent 协助检查。

主要功能

  1. 01测试消息路由与转换
  2. 02验证 DLQ 和重试行为
  3. 03检查消息顺序与关联 ID
  4. 04测试错误处理和补偿流程
  5. 05测试协议中介与 EIP 模式
  6. 06通过 Agent 协调中间件检查

适用场景

  1. 01测试消息代理路由及目标队列
  2. 02验证转换后的消息模式
  3. 03检查毒消息处理与重试
  4. 04用关联 ID 跟踪多跳流程

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/proffesor-for-testing/agentic-qe --skill qe-middleware-testing-patterns

安装后在 Claude Code 中输入/qe-middleware-testing-patterns确认已加载。

详细介绍

中间件测试模式

<default_to_action> 测试企业中间件、ESB 或消息驱动系统时:

  1. 验证消息路由逻辑(基于内容、基于标头、收件人列表)
  2. 端到端测试消息转换(输入格式 -> 输出格式)
  3. 验证死信队列处理(有毒消息、重试耗尽)
  4. 使用关联 ID 断言消息顺序和序列
  5. 演练错误处理和补偿模式
  6. 测试协议中介(SOAP 到 REST、同步到异步)
  7. 验证 EIP 模式(拆分器、聚合器、内容增强器、规范化器)

快速模式选择:

  • 消息路由问题 -> 基于内容的路由器测试
  • 转换失败 -> 输入架构/输出架构验证
  • 消息丢失 -> DLQ 和重试模式测试
  • 协议桥接 -> 中介往返测试
  • 复杂流程 -> 关联 ID 跟踪测试

关键成功因素:

  • 中间件正常工作时是不可见的;要测试这些不可见之处
  • 务必同时验证正常路径和错误通道(DLQ)
  • 关联 ID 是跟踪多跳消息的生命线 </default_to_action>

快速参考卡

适用场景
  • 测试 ESB 消息流(IBM IIB/ACE、MuleSoft、WSO2)
  • 验证消息转换(XSLT、JSON 到 XML、平面文件解析)
  • 测试消息代理路由(MQ、Kafka、RabbitMQ)
  • 验证死信队列行为
  • 测试协议中介(SOAP/REST 桥接)
  • 验证企业集成模式(EIP)
测试级别
级别目的依赖项速度
单元转换单个映射的正确性无快
路由逻辑路由决策的准确性模拟端点快
集成端到端消息流代理 + 端点中等
DLQ/错误错误处理和恢复完整中间件堆栈较慢
关键测试场景
场景必测项示例
路由正确选择目标订单类型 A -> 队列 A,类型 B -> 队列 B
转换映射后的架构合规性XML -> JSON 字段映射准确性
DLQ有毒消息处理格式错误的 XML 进入 DLQ,而非丢失
顺序保持序列消息 1-2-3 按顺序到达
关联多跳跟踪请求-回复通过关联 ID 匹配
重试瞬时故障恢复退避重试 3 次,然后进入 DLQ
中介协议桥接保真度SOAP 请求生成正确的 REST 调用
工具
  • 消息代理:IBM MQ、RabbitMQ、Apache Kafka、ActiveMQ
  • ESB:IBM IIB/ACE、MuleSoft、WSO2、Apache Camel
  • 测试:SoapUI、Postman、自定义测试工具
  • 虚拟化:Mountebank、WireMock、HoverFly
  • 监控:Splunk、ELK、Datadog
Agent 协作
  • qe-middleware-validator:验证路由规则、转换准确性和 EIP 模式
  • qe-message-broker-tester:测试代理连接、DLQ 行为和消息顺序
  • qe-soap-tester:SOAP/WSDL 验证、WS-Security、协议中介

消息路由模式测试

基于内容的路由器
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');
  });
});
基于标头的路由器
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');
  });
});
收件人列表
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');
  });
});

消息转换测试

JSON 到 XML 转换
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');
  });
});
平面文件到 XML 转换
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');
  });
});

死信队列(DLQ)测试

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');
  });
});

消息顺序和序列

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 < 10; i++) {
      const msg = await broker.consume('orders.sequenced.out', { timeout: 5000 });
      received.push(msg.body.sequence);
    }

    expect(received).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
  });

  it('detects out-of-order messages via sequence gap', async () => {
    // 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 错误处理和补偿

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');
  });
});

协议中介测试

SOAP 到 REST 中介
describe('SOAP to REST Protocol Mediation', () => {
  it('converts SOAP request to REST call and back', async () => {
    const soapRequest = `
      <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                        xmlns:ord="http://example.com/orders">
        <soapenv:Body>
          <ord:GetOrder>
            <ord:OrderId>ORD-700</ord:OrderId>
          </ord:GetOrder>
        </soapenv:Body>
      </soapenv:Envelope>
    `;

    // 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');
  });
});
同步到异步中介
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');
  });
});

企业集成模式(EIP)

拆分器模式
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 },
        { sku: 'B2', qty: 5 },
        { sku: 'C3', qty: 20 }
      ]
    };

    await broker.publish('orders.batch', { body: batchOrder });

    const splitMessages = [];
    for (let i = 0; i < 3; i++) {
      splitMessages.push(await broker.consume('orders.items', { timeout: 5000 }));
    }

    expect(splitMessages).toHaveLength(3);
    expect(splitMessages.map(m => m.body.sku).sort()).toEqual(['A1', 'B2', 'C3']);

    // Each split message retains parent correlation
    for (const msg of splitMessages) {
      expect(msg.headers['x-batch-id']).toBe('BATCH-001');
      expect(msg.headers['x-split-total']).toBe('3');
    }
  });
});
聚合器模式
describe('Aggregator - Collect Item Results', () => {
  it('aggregates individual item results into batch response', async () => {
    const batchId = 'BATCH-002';
    const itemResults = [
      { sku: 'A1', status: 'PICKED', batchId },
      { sku: 'B2', status: 'PICKED', batchId },
      { sku: 'C3', status: 'PICKED', batchId }
    ];

    for (const result of itemResults) {
      await broker.publish('items.completed', {
        headers: { 'x-batch-id': batchId, 'x-split-total': '3' },
        body: result
      });
    }

    const aggregated = await broker.consume('orders.batch.completed', { timeout: 10000 });

    expect(aggregated.body.batchId).toBe('BATCH-002');
    expect(aggregated.body.items).toHaveLength(3);
    expect(aggregated.body.items.every(i => i.status === 'PICKED')).toBe(true);
  });

  it('handles aggregation timeout for incomplete batches', async () => {
    const batchId = 'BATCH-TIMEOUT';

    // Only send 2 of 3 expected items
    for (let i = 0; i < 2; i++) {
      await broker.publish('items.completed', {
        headers: { 'x-batch-id': batchId, 'x-split-total': '3' },
        body: { sku: `ITEM-${i}`, status: 'PICKED', batchId }
      });
    }

    // After timeout, aggregator should emit partial result with error
    const partial = await broker.consume('orders.batch.incomplete', { timeout: 60000 });
    expect(partial.body.batchId).toBe('BATCH-TIMEOUT');
    expect(partial.body.receivedCount).toBe(2);
    expect(partial.body.expectedCount).toBe(3);
  });
});
内容增强器模式
describe('Content Enricher - Customer Data Lookup', () => {
  it('enriches order with customer details from CRM', async () => {
    mockCrm.onGet('/customers/CUST-50').reply(200, {
      name: 'Acme Corp',
      tier: 'GOLD',
      creditLimit: 50000
    });

    await broker.publish('orders.enrich', {
      body: { orderId: 'ORD-ENRICH', customerId: 'CUST-50', total: 1200 }
    });

    const enriched = await broker.consume('orders.enriched', { timeout: 5000 });

    expect(enriched.body.orderId).toBe('ORD-ENRICH');
    expect(enriched.body.customer.name).toBe('Acme Corp');
    expect(enriched.body.customer.tier).toBe('GOLD');
    expect(enriched.body.creditCheckPassed).toBe(true); // 1200 < 50000
  });
});

重试和断路器模式

describe('Retry with Exponential Backoff', () => {
  it('retries 3 times with increasing delays before DLQ', async () => {
    const timestamps = [];
    mockEndpoint.onPost('/process').reply(() => {
      timestamps.push(Date.now());
      return [500, 'Temporary failure'];
    });

    await broker.publish('orders.process', { body: { orderId: 'ORD-RETRY' } });

    await broker.consume('orders.dlq', { timeout: 60000 });

    // Verify exponential backoff timing (1s, 2s, 4s approximately)
    expect(timestamps).toHaveLength(4); // 1 initial + 3 retries
    const delay1 = timestamps[1] - timestamps[0];
    const delay2 = timestamps[2] - timestamps[1];
    expect(delay1).toBeGreaterThan(800);  // ~1s
    expect(delay2).toBeGreaterThan(1800); // ~2s
  });
});

describe('Circuit Breaker', () => {
  it('opens circuit after threshold failures', async () => {
    // Fail 5 messages to trip circuit breaker (threshold = 5)
    mockEndpoint.onPost('/process').reply(500, 'Down');

    for (let i = 0; i < 5; i++) {
      await broker.publish('orders.circuit', { body: { orderId: `ORD-CB-${i}` } });
    }

    // Wait for circuit to open
    await sleep(5000);

    // Next message should be immediately rejected (circuit open)
    const startTime = Date.now();
    await broker.publish('orders.circuit', { body: { orderId: 'ORD-CB-FAST-FAIL' } });
    const dlq = await broker.consume('orders.circuit.dlq', { timeout: 3000 });
    const elapsed = Date.now() - startTime;

    expect(dlq.headers['x-circuit-state']).toBe('OPEN');
    expect(elapsed).toBeLessThan(2000); // Fast fail, no retry
  });
});

用于隔离测试的服务虚拟化

describe('Service Virtualization - Middleware Isolation', () => {
  let virtualService;

  beforeAll(async () => {
    virtualService = await mountebank.createImposter({
      port: 4545,
      protocol: 'http',
      stubs: [
        {
          predicates: [{ equals: { method: 'POST', path: '/sap/orders' } }],
          responses: [{
            is: {
              statusCode: 200,
              headers: { 'Content-Type': 'application/xml' },
              body: '<OrderResponse><Status>CREATED</Status><SapOrderId>SAP-001</SapOrderId></OrderResponse>'
            }
          }]
        },
        {
          predicates: [{ equals: { method: 'POST', path: '/sap/orders' },
                         contains: { body: '<InvalidField>' } }],
          responses: [{
            is: { statusCode: 400, body: '<Error>Invalid request</Error>' }
          }]
        }
      ]
    });
  });

  afterAll(async () => {
    await mountebank.deleteImposter(4545);
  });

  it('middleware correctly handles SAP success response', async () => {
    await broker.publish('orders.to-sap', {
      body: { orderId: 'ORD-VIRT', customer: 'CUST-01' }
    });

    const result = await broker.consume('orders.sap-confirmed', { timeout: 5000 });
    expect(result.body.sapOrderId).toBe('SAP-001');
  });
});

最佳实践

要做
  • 对每个流程同时测试正常路径和错误通道(DLQ)
  • 使用关联 ID 跟踪消息在多跳中间件中的流转
  • 在每个转换边界验证架构合规性
  • 使用分区队列/有序队列测试消息顺序
  • 使用服务虚拟化将中间件与后端系统隔离
  • 测试重试耗尽,确认 DLQ 行为
  • 验证多步骤流程的补偿模式(Saga 回滚)
避免
  • 只测试正常路径,忽略 DLQ/错误队列
  • 对队列名称进行硬编码;使用配置驱动的测试
  • 忽略消息顺序要求
  • 跳过转换边缘情况(空字段、特殊字符、编码)
  • 测试中间件时不验证关联 ID
  • 未验证退避时间,就假设重试正常工作
  • 未测试熔断器阈值就进行部署

Agent 辅助的中间件测试

// Validate middleware routing rules
await Task("Middleware Routing Validation", {
  routingConfig: 'esb/routing-rules.xml',
  messageTypes: ['STANDARD_ORDER', 'EXPRESS_ORDER', 'RETURN'],
  validateDLQ: true,
  checkCorrelationIds: true
}, "qe-middleware-validator");

// Test message broker behavior
await Task("Message Broker DLQ Test", {
  broker: 'rabbitmq',
  queues: ['orders.inbound', 'orders.process', 'orders.dlq'],
  retryPolicy: { maxRetries: 3, backoff: 'exponential' },
  poisonMessages: ['malformed-xml', 'missing-required-fields', 'oversized-payload']
}, "qe-message-broker-tester");

// SOAP/WSDL contract validation
await Task("SOAP Service Contract Validation", {
  wsdl: 'services/OrderService.wsdl',
  endpoints: ['CreateOrder', 'GetOrderStatus', 'CancelOrder'],
  validateWsSecurity: true,
  testFaultHandling: true
}, "qe-soap-tester");

// Transformation accuracy testing
await Task("Transformation Accuracy Suite", {
  transformations: [
    { name: 'JSON-to-SAP-XML', input: 'testdata/order.json', expected: 'testdata/order-sap.xml' },
    { name: 'Flat-to-XML', input: 'testdata/inventory.dat', expected: 'testdata/inventory.xml' }
  ],
  validateSchemas: true,
  edgeCases: ['empty-fields', 'special-chars', 'max-length']
}, "qe-middleware-validator");

Agent 协调提示

内存命名空间
aqe/middleware-testing/
  routing/           - Routing rule configurations and test results
  transformations/   - Mapping definitions and validation results
  dlq/               - DLQ test results and replay outcomes
  eip-patterns/      - EIP pattern test coverage
  circuit-breakers/  - Circuit breaker state and threshold configs
  correlation/       - Correlation ID tracing results
集群协调
const middlewareFleet = await FleetManager.coordinate({
  strategy: 'middleware-testing',
  agents: [
    'qe-middleware-validator',   // Routing, transformation, EIP patterns
    'qe-message-broker-tester',  // DLQ, ordering, retry, circuit breaker
    'qe-soap-tester'             // SOAP contracts, WS-Security, mediation
  ],
  topology: 'mesh'
});

await middlewareFleet.execute({
  flows: [
    { name: 'order-inbound', stages: ['route', 'transform', 'enrich', 'deliver'] },
    { name: 'payment-processing', stages: ['validate', 'authorize', 'settle'] }
  ],
  testDLQ: true,
  testCompensation: true
});

相关技能


请记住

中间件是企业架构的神经系统。它正常工作时,无人察觉;一旦失效,一切都会崩溃。测试路由决策、转换、错误通道和补偿模式。始终使用关联 ID 验证多跳流程,并始终确认毒消息会进入 DLQ,且包含足够的元数据以便诊断和重放。

**使用 Agent:**Agent 会根据消息类型验证路由规则,测试符合架构要求的转换准确性,并系统地测试 DLQ/重试/熔断器行为。使用 Agent 覆盖组合数量庞大的路由路径和转换边缘情况,这些情况是手动测试无法触及的。

评论

支持 Markdown