@villedemontreal/correlation-id
Version:
Express middleware to set a correlation in Express. The correlation id will be consistent across async calls within the handling of a request.
354 lines (314 loc) • 10.7 kB
text/typescript
import { assert } from 'chai';
import * as express from 'express';
import * as request from 'supertest';
import { setTimeout as delay } from 'timers/promises';
import { correlationIdService } from '../services/correlationIdService';
import { setTestingConfigurations } from '../utils/testingConfigurations';
import { createCorrelationIdMiddleware } from './correlationIdMiddleware';
// ==========================================
// Set Testing configurations
// ==========================================
setTestingConfigurations();
const uuidMatcher = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
// ==========================================
// Correlation ID service
// ==========================================
describe('Correlation ID Middleware', () => {
it('should generate correlation id if it doesnt exists', async () => {
const app: express.Application = express();
app.use(createCorrelationIdMiddleware());
app.get('/', (req, res) => {
const actual = correlationIdService.getId();
assert.match(actual, uuidMatcher);
res.end();
});
await request(app).get('/').send();
});
it('should get correlation id from incoming request', async () => {
const testId = 'correlation-id-123';
const app: express.Application = express();
app.use(createCorrelationIdMiddleware());
app.get('/', (req, res) => {
const actual = correlationIdService.getId();
assert.strictEqual(
actual,
testId,
'getId() should return id from x-correlation-id header of inbound request',
);
res.end();
});
await request(app).get('/').set('X-Correlation-ID', testId).send();
});
it('should maintain correlation id with async callbacks', async () => {
const testId = 'correlation-id-123';
let actual = '';
let actual2 = '';
const app: express.Application = express();
app.use(createCorrelationIdMiddleware());
app.get('/', async (req, res) => {
actual = correlationIdService.getId();
setTimeout(() => {
actual2 = correlationIdService.getId();
res.end();
}, 250);
});
assert.isUndefined(correlationIdService.getId());
await request(app).get('/').set('X-Correlation-ID', testId).send();
assert.isUndefined(correlationIdService.getId());
assert.strictEqual(
actual,
testId,
'getId() should return id from x-correlation-id header of inbound request',
);
assert.strictEqual(
actual2,
testId,
'getId() should return id from x-correlation-id header of inbound request',
);
});
it('should maintain correlation id with async/await operations', async () => {
const testId = 'correlation-id-123';
let actual = '';
let actual2 = '';
const app: express.Application = express();
app.use(createCorrelationIdMiddleware());
app.get('/', async (req, res, next) => {
try {
actual = correlationIdService.getId();
await delay(250);
actual2 = correlationIdService.getId();
} catch (e) {
next(e);
} finally {
res.end();
}
});
assert.isUndefined(correlationIdService.getId());
await request(app).get('/').set('X-Correlation-ID', testId).send();
assert.isUndefined(correlationIdService.getId());
assert.strictEqual(
actual,
testId,
'getId() should return id from x-correlation-id header of inbound request',
);
assert.strictEqual(
actual2,
testId,
'getId() should return id from x-correlation-id header of inbound request',
);
});
it('should keep correlation ids in nested requests', async () => {
const testId = 'correlation-id-123';
const testId2 = 'correlation-id-456';
let actual = '';
let actual2 = '';
let actual3 = '';
let actual4 = '';
let actual5 = '';
const app: express.Application = express();
app.use(createCorrelationIdMiddleware());
app.get('/', async (req, res, next) => {
try {
actual = correlationIdService.getId();
await delay(250);
actual2 = correlationIdService.getId();
await request(app).get('/foo').set('X-Correlation-ID', testId2).send();
actual3 = correlationIdService.getId();
} catch (e) {
next(e);
} finally {
res.end();
}
});
app.get('/foo', async (req, res, next) => {
try {
actual4 = correlationIdService.getId();
await delay(250);
actual5 = correlationIdService.getId();
} catch (e) {
next(e);
} finally {
res.end();
}
});
assert.isUndefined(correlationIdService.getId());
await request(app).get('/').set('X-Correlation-ID', testId).send();
assert.isUndefined(correlationIdService.getId());
assert.strictEqual(
actual,
testId,
'getId() should return id from x-correlation-id header of inbound request',
);
assert.strictEqual(
actual2,
testId,
'getId() should return id from x-correlation-id header of inbound request',
);
assert.strictEqual(
actual3,
testId,
'getId() should return id from x-correlation-id header of inbound request',
);
assert.strictEqual(
actual4,
testId2,
'getId() should return id from x-correlation-id header of inbound request',
);
assert.strictEqual(
actual5,
testId2,
'getId() should return id from x-correlation-id header of inbound request',
);
});
it('should keep correlation ids separated in parallel requests', async function () {
this.timeout(5000);
const testId = 'correlation-id-123';
const testId2 = 'correlation-id-456';
let actual = '';
let actual2 = '';
let actual4 = '';
let actual5 = '';
let unlock: any;
const lock = new Promise<void>((resolve) => {
unlock = resolve;
});
const app: express.Application = express();
app.use(createCorrelationIdMiddleware());
app.get('/', async (req, res, next) => {
try {
actual = correlationIdService.getId();
console.log('start of req1', actual);
await lock;
actual2 = correlationIdService.getId();
console.log('end of req1', actual2);
} catch (e) {
next(e);
} finally {
res.end();
}
});
app.get('/foo', async (req, res, next) => {
try {
actual4 = correlationIdService.getId();
console.log('start of req2', actual4);
unlock();
await delay(200);
actual5 = correlationIdService.getId();
console.log('end of req2', actual5);
} catch (e) {
next(e);
} finally {
res.end();
}
});
console.log('send req1');
assert.isUndefined(correlationIdService.getId());
const req1 = request(app).get('/').set('X-Correlation-ID', testId).send();
console.log('send req2');
assert.isUndefined(correlationIdService.getId());
const req2 = request(app).get('/foo').set('X-Correlation-ID', testId2).send();
await Promise.all([req1, req2]);
assert.isUndefined(correlationIdService.getId());
assert.strictEqual(
actual,
testId,
'getId() should return id from x-correlation-id header of inbound request',
);
assert.strictEqual(
actual2,
testId,
'getId() should return id from x-correlation-id header of inbound request',
);
assert.strictEqual(
actual4,
testId2,
'getId() should return id from x-correlation-id header of inbound request',
);
assert.strictEqual(
actual5,
testId2,
'getId() should return id from x-correlation-id header of inbound request',
);
});
it('should work with operations causing errors', async () => {
const testId = 'correlation-id-123';
let actual = '';
const app: express.Application = express();
app.use(createCorrelationIdMiddleware());
app.get('/', () => {
actual = correlationIdService.getId();
throw new Error('some error');
});
assert.isUndefined(correlationIdService.getId());
const result = await request(app).get('/').set('X-Correlation-ID', testId).send();
assert.strictEqual(result.status, 500);
assert.isUndefined(correlationIdService.getId());
assert.strictEqual(
actual,
testId,
'getId() should return id from x-correlation-id header of inbound request',
);
});
it('a filter can be specified', async () => {
const testId = 'correlation-id-123';
const filter = (req: express.Request): boolean => {
if (req.path.startsWith('/ok/')) {
return true;
}
return false;
};
const app: express.Application = express();
app.use(createCorrelationIdMiddleware(filter));
app.get('/ok/1', (req, res) => {
const actual = correlationIdService.getId();
assert.strictEqual(
actual,
testId,
'getId() should return id from x-correlation-id header of inbound request',
);
res.end();
});
app.get('/ok/2', (req, res) => {
const actual = correlationIdService.getId();
assert.strictEqual(
actual,
testId,
'getId() should return id from x-correlation-id header of inbound request',
);
res.end();
});
app.get('/notok/1', (req, res) => {
const actual = correlationIdService.getId();
assert.isUndefined(actual);
res.end();
});
await request(app).get('/ok/1').set('X-Correlation-ID', testId).send();
await request(app).get('/ok/2').set('X-Correlation-ID', testId).send();
await request(app).get('/notok/1').set('X-Correlation-ID', testId).send();
});
it('getCidInfo() cid received', async () => {
const testId = 'correlation-id-123';
const app: express.Application = express();
app.use(createCorrelationIdMiddleware());
app.get('/', (req, res) => {
const info = correlationIdService.getCidInfo(req);
assert.strictEqual(info.current, testId);
assert.strictEqual(info.receivedInRequest, testId);
assert.isUndefined(info.generated);
res.end();
});
await request(app).get('/').set('X-Correlation-ID', testId).send();
});
it('getCidInfo() cid generated', async () => {
const app: express.Application = express();
app.use(createCorrelationIdMiddleware());
app.get('/', (req, res) => {
const info = correlationIdService.getCidInfo(req);
assert.match(info.current, uuidMatcher);
assert.strictEqual(info.current, info.generated);
assert.isUndefined(info.receivedInRequest);
res.end();
});
await request(app).get('/').send();
});
});