Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions src/utils/__tests__/correlationId.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {
generateCorrelationId,
getOrGenerateCorrelationId,
isValidCorrelationId,
createContext,
} from "../correlationId";

describe("correlationId utility", () => {
describe("generateCorrelationId", () => {
it("should generate a valid correlation ID", () => {
const id = generateCorrelationId();
expect(id).toMatch(/^corr-/);
expect(id.length).toBeGreaterThan(15);
});

it("should generate unique IDs", () => {
const ids = new Set();
for (let i = 0; i < 100; i++) {
ids.add(generateCorrelationId());
}
expect(ids.size).toBe(100);
});
});

describe("isValidCorrelationId", () => {
it("should validate correct format", () => {
expect(isValidCorrelationId("corr-abc123-def456-ghi789")).toBe(true);
expect(isValidCorrelationId("corr-1a2b3c4d-5e6f-7g8h")).toBe(true);
});

it("should reject invalid formats", () => {
expect(isValidCorrelationId("invalid")).toBe(false);
expect(isValidCorrelationId("")).toBe(false);
expect(isValidCorrelationId("corr-")).toBe(false);
});
});

describe("getOrGenerateCorrelationId", () => {
it("should return existing valid correlation ID from headers", () => {
const headers = { "x-correlation-id": "corr-test123-abc456-def789" };
const result = getOrGenerateCorrelationId(headers);
expect(result).toBe("corr-test123-abc456-def789");
});

it("should generate new ID when header is missing", () => {
const headers = {};
const result = getOrGenerateCorrelationId(headers);
expect(isValidCorrelationId(result)).toBe(true);
});

it("should generate new ID when header is invalid", () => {
const headers = { "x-correlation-id": "invalid-id" };
const result = getOrGenerateCorrelationId(headers);
expect(isValidCorrelationId(result)).toBe(true);
});
});

describe("createContext", () => {
it("should create context with correlation ID and timestamp", () => {
const headers = {};
const context = createContext(headers);
expect(context.correlationId).toBeDefined();
expect(context.timestamp).toMatch(/^\d{4}-\d{2}-\d{2}T/);
});
});
});
50 changes: 50 additions & 0 deletions src/utils/correlationId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* Correlation ID utility for distributed tracing and request tracking
* Generates unique identifiers to track requests across services
*/

/**
* Generates a correlation ID for distributed request tracing
* Format: 8-4-4-4-12 (UUID-like but optimized for tracing)
*/
export function generateCorrelationId(): string {
const timestamp = Date.now().toString(36);
const randomPart = Math.random().toString(36).substring(2, 10);
const processId = process.pid.toString(36).padStart(4, "0");
return `corr-${timestamp}-${randomPart}-${processId}`;
}

/**
* Extracts correlation ID from request headers or generates a new one
*/
export function getOrGenerateCorrelationId(
headers: Record<string, string | undefined>
): string {
const existing = headers["x-correlation-id"];
if (existing && isValidCorrelationId(existing)) {
return existing;
}
return generateCorrelationId();
}

/**
* Validates a correlation ID format
*/
export function isValidCorrelationId(id: string): boolean {
return /^corr-[a-z0-9]{4,}-[a-z0-9]{4,}-[a-z0-9]{4,}$/.test(id);
}

/**
* Extracts correlation ID for logging context
*/
export interface CorrelationContext {
correlationId: string;
timestamp: string;
}

export function createContext(headers: Record<string, string | undefined>): CorrelationContext {
return {
correlationId: getOrGenerateCorrelationId(headers),
timestamp: new Date().toISOString(),
};
}
Loading