autotel
Version:
Write Once, Observe Anywhere
409 lines (406 loc) • 13.1 kB
JavaScript
;
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