UNPKG

autotel

Version:
409 lines (406 loc) 13.1 kB
'use strict'; require('./chunk-JEQ2X3Z6.cjs'); // src/messaging-testing.ts function randomHex(length) { let result = ""; const chars = "0123456789abcdef"; for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; } function createMessagingTestHarness() { const producerCalls = []; const consumerCalls = []; const rebalanceEvents = []; return { producerCalls, consumerCalls, rebalanceEvents, recordProducerCall(call) { producerCalls.push({ ...call, timestamp: Date.now() }); }, recordConsumerCall(call) { consumerCalls.push({ ...call, timestamp: Date.now() }); }, recordRebalanceEvent(event) { rebalanceEvents.push(event); }, createMockMessage(payload, options = {}) { return { payload, headers: options.headers ?? this.createMockTraceHeaders(), offset: options.offset ?? Math.floor(Math.random() * 1e4), partition: options.partition ?? 0, key: options.key, messageId: options.messageId ?? `msg-${randomHex(8)}`, timestamp: options.timestamp ?? Date.now() }; }, createMockTraceHeaders(traceId, spanId) { const tid = traceId ?? randomHex(32); const sid = spanId ?? randomHex(16); return { traceparent: `00-${tid}-${sid}-01` }; }, assertProducerCalled(destination, options = {}) { const calls = producerCalls.filter((c) => c.destination === destination); if (calls.length === 0) { throw new Error( `Expected producer to be called for destination '${destination}', but it was not called` ); } if (options.messageCount !== void 0 && calls.length !== options.messageCount) { throw new Error( `Expected ${options.messageCount} producer calls for '${destination}', got ${calls.length}` ); } if (options.hasTraceHeaders) { const withoutHeaders = calls.filter((c) => !c.headers?.traceparent); if (withoutHeaders.length > 0) { throw new Error( `Expected all producer calls for '${destination}' to have trace headers, but ${withoutHeaders.length} did not` ); } } if (options.traceId) { const matchingTraceId = calls.filter( (c) => c.traceId === options.traceId ); if (matchingTraceId.length === 0) { throw new Error( `Expected producer call for '${destination}' with traceId '${options.traceId}', but none found` ); } } if (options.payloadMatcher) { const matching = calls.filter( (c) => options.payloadMatcher(c.payload) ); if (matching.length === 0) { throw new Error( `Expected producer call for '${destination}' to match payload matcher, but none did` ); } } }, assertProducerNotCalled(destination) { if (destination) { const calls = producerCalls.filter( (c) => c.destination === destination ); if (calls.length > 0) { throw new Error( `Expected producer not to be called for '${destination}', but it was called ${calls.length} times` ); } } else { if (producerCalls.length > 0) { throw new Error( `Expected no producer calls, but ${producerCalls.length} calls were made` ); } } }, assertConsumerProcessed(destination, options = {}) { const calls = consumerCalls.filter((c) => c.destination === destination); if (calls.length === 0) { throw new Error( `Expected consumer to process messages for destination '${destination}', but none were processed` ); } if (options.messageCount !== void 0 && calls.length !== options.messageCount) { throw new Error( `Expected ${options.messageCount} consumer calls for '${destination}', got ${calls.length}` ); } if (options.consumerGroup) { const wrongGroup = calls.filter( (c) => c.consumerGroup !== options.consumerGroup ); if (wrongGroup.length > 0) { throw new Error( `Expected consumer group '${options.consumerGroup}' for '${destination}', but found different groups` ); } } if (options.hasProducerLinks) { const withoutLinks = calls.filter((c) => c.producerLinks.length === 0); if (withoutLinks.length > 0) { throw new Error( `Expected all consumer calls for '${destination}' to have producer links, but ${withoutLinks.length} did not` ); } } if (options.hasDuplicates !== void 0) { const duplicates = calls.filter((c) => c.isDuplicate); if (options.hasDuplicates && duplicates.length === 0) { throw new Error( `Expected duplicate messages for '${destination}', but none were detected` ); } if (!options.hasDuplicates && duplicates.length > 0) { throw new Error( `Expected no duplicate messages for '${destination}', but ${duplicates.length} were detected` ); } } if (options.hasOutOfOrder !== void 0) { const outOfOrder = calls.filter((c) => c.outOfOrderInfo !== null); if (options.hasOutOfOrder && outOfOrder.length === 0) { throw new Error( `Expected out-of-order messages for '${destination}', but none were detected` ); } if (!options.hasOutOfOrder && outOfOrder.length > 0) { throw new Error( `Expected no out-of-order messages for '${destination}', but ${outOfOrder.length} were detected` ); } } if (options.hasDLQ !== void 0) { const dlqCalls = calls.filter((c) => c.dlqReason !== void 0); if (options.hasDLQ && dlqCalls.length === 0) { throw new Error( `Expected DLQ routing for '${destination}', but none occurred` ); } if (!options.hasDLQ && dlqCalls.length > 0) { throw new Error( `Expected no DLQ routing for '${destination}', but ${dlqCalls.length} occurred` ); } } }, assertConsumerNotCalled(destination) { if (destination) { const calls = consumerCalls.filter( (c) => c.destination === destination ); if (calls.length > 0) { throw new Error( `Expected consumer not to be called for '${destination}', but it processed ${calls.length} messages` ); } } else { if (consumerCalls.length > 0) { throw new Error( `Expected no consumer calls, but ${consumerCalls.length} messages were processed` ); } } }, assertRebalanceOccurred(destination, type, partitionCount) { const events = rebalanceEvents.filter( (e) => e.destination === destination && e.type === type ); if (events.length === 0) { throw new Error( `Expected rebalance '${type}' for '${destination}', but none occurred` ); } if (partitionCount !== void 0) { const matching = events.filter( (e) => e.partitions.length === partitionCount ); if (matching.length === 0) { throw new Error( `Expected rebalance '${type}' for '${destination}' with ${partitionCount} partitions, but none matched` ); } } }, getProducerCalls(destination) { if (destination) { return producerCalls.filter((c) => c.destination === destination); } return [...producerCalls]; }, getConsumerCalls(destination) { if (destination) { return consumerCalls.filter((c) => c.destination === destination); } return [...consumerCalls]; }, getLastProducerCall(destination) { const calls = this.getProducerCalls(destination); return calls.at(-1); }, getLastConsumerCall(destination) { const calls = this.getConsumerCalls(destination); return calls.at(-1); }, reset() { producerCalls.length = 0; consumerCalls.length = 0; rebalanceEvents.length = 0; }, shutdown() { this.reset(); } }; } function createMockMessageBroker() { const topics = /* @__PURE__ */ new Map(); return { topics, publish(topic, message) { if (!topics.has(topic)) { topics.set(topic, []); } topics.get(topic).push({ ...message, timestamp: message.timestamp ?? Date.now(), offset: message.offset ?? topics.get(topic).length }); }, consume(topic, count) { const messages = topics.get(topic) ?? []; if (count === void 0) { const all = [...messages]; messages.length = 0; return all; } return messages.splice(0, count); }, peek(topic, count) { const messages = topics.get(topic) ?? []; if (count === void 0) { return [...messages]; } return messages.slice(0, count); }, getMessageCount(topic) { return topics.get(topic)?.length ?? 0; }, clear(topic) { if (topic) { topics.set(topic, []); } else { topics.clear(); } }, createTopic(topic) { if (!topics.has(topic)) { topics.set(topic, []); } }, deleteTopic(topic) { topics.delete(topic); }, listTopics() { return [...topics.keys()]; } }; } function extractTraceIdFromHeader(traceparent) { const parts = traceparent.split("-"); if (parts.length >= 3 && parts[1] !== void 0) { return parts[1]; } return null; } function extractSpanIdFromHeader(traceparent) { const parts = traceparent.split("-"); if (parts.length >= 4 && parts[2] !== void 0) { return parts[2]; } return null; } function createMockSpanContext(traceId, spanId) { return { traceId: traceId ?? randomHex(32), spanId: spanId ?? randomHex(16), traceFlags: 1, isRemote: true }; } function createMockProducerLink(traceId, spanId) { return { context: createMockSpanContext(traceId, spanId), attributes: { "messaging.link.source": "producer" } }; } function createMockMessageBatch(payloads, options = {}) { const startOffset = options.startOffset ?? 0; const addTraceHeaders = options.addTraceHeaders ?? true; const traceId = options.traceId ?? randomHex(32); return payloads.map((payload, index) => ({ payload, headers: addTraceHeaders ? { traceparent: `00-${traceId}-${randomHex(16)}-01` } : void 0, offset: startOffset + index, partition: options.partition ?? 0, messageId: `msg-${randomHex(8)}`, timestamp: Date.now() + index })); } function createRebalanceScenario(topic, consumerGroup, partitions) { const assignments = partitions.map((p) => ({ topic, partition: p, offset: 0 })); return { assignEvent: { type: "assigned", partitions: assignments, timestamp: Date.now(), generation: 1, destination: topic, consumerGroup }, revokeEvent: { type: "revoked", partitions: assignments, timestamp: Date.now() + 1e3, generation: 2, destination: topic, consumerGroup } }; } function createOutOfOrderScenario(payloads, outOfOrderIndices) { const messages = createMockMessageBatch(payloads, { addTraceHeaders: true }); const shuffled = [...messages]; for (const index of outOfOrderIndices) { if (index > 0 && index < shuffled.length) { const prev = shuffled[index - 1]; const curr = shuffled[index]; shuffled[index - 1] = curr; shuffled[index] = prev; } } return shuffled; } function createDuplicateScenario(payloads, duplicateIndices) { const messages = createMockMessageBatch(payloads, { addTraceHeaders: true }); const result = [...messages]; for (const index of duplicateIndices) { const originalMessage = messages[index]; if (index >= 0 && index < messages.length && originalMessage) { result.splice(index + 1, 0, { ...originalMessage }); } } return result; } exports.createDuplicateScenario = createDuplicateScenario; exports.createMessagingTestHarness = createMessagingTestHarness; exports.createMockMessageBatch = createMockMessageBatch; exports.createMockMessageBroker = createMockMessageBroker; exports.createMockProducerLink = createMockProducerLink; exports.createMockSpanContext = createMockSpanContext; exports.createOutOfOrderScenario = createOutOfOrderScenario; exports.createRebalanceScenario = createRebalanceScenario; exports.extractSpanIdFromHeader = extractSpanIdFromHeader; exports.extractTraceIdFromHeader = extractTraceIdFromHeader; //# sourceMappingURL=messaging-testing.cjs.map //# sourceMappingURL=messaging-testing.cjs.map