@into-cps-association/logger-ms 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +3 -0
- package/README.md +198 -0
- package/dist/src/app.controller.d.ts +12 -0
- package/dist/src/app.controller.js +53 -0
- package/dist/src/app.controller.js.map +1 -0
- package/dist/src/app.module.d.ts +2 -0
- package/dist/src/app.module.js +44 -0
- package/dist/src/app.module.js.map +1 -0
- package/dist/src/config/certificates.d.ts +5 -0
- package/dist/src/config/certificates.js +47 -0
- package/dist/src/config/certificates.js.map +1 -0
- package/dist/src/config/cli.d.ts +1 -0
- package/dist/src/config/cli.js +45 -0
- package/dist/src/config/cli.js.map +1 -0
- package/dist/src/config/config.interface.d.ts +17 -0
- package/dist/src/config/config.interface.js +2 -0
- package/dist/src/config/config.interface.js.map +1 -0
- package/dist/src/config/config.service.d.ts +21 -0
- package/dist/src/config/config.service.js +275 -0
- package/dist/src/config/config.service.js.map +1 -0
- package/dist/src/config/cors.d.ts +7 -0
- package/dist/src/config/cors.js +29 -0
- package/dist/src/config/cors.js.map +1 -0
- package/dist/src/config/util.d.ts +1 -0
- package/dist/src/config/util.js +5 -0
- package/dist/src/config/util.js.map +1 -0
- package/dist/src/dto/log-event.dto.d.ts +37 -0
- package/dist/src/dto/log-event.dto.js +125 -0
- package/dist/src/dto/log-event.dto.js.map +1 -0
- package/dist/src/log-event-validation.pipe.d.ts +4 -0
- package/dist/src/log-event-validation.pipe.js +23 -0
- package/dist/src/log-event-validation.pipe.js.map +1 -0
- package/dist/src/logger-auth.guard.d.ts +7 -0
- package/dist/src/logger-auth.guard.js +46 -0
- package/dist/src/logger-auth.guard.js.map +1 -0
- package/dist/src/logs/logs.module.d.ts +2 -0
- package/dist/src/logs/logs.module.js +19 -0
- package/dist/src/logs/logs.module.js.map +1 -0
- package/dist/src/logs/logs.service.d.ts +29 -0
- package/dist/src/logs/logs.service.js +201 -0
- package/dist/src/logs/logs.service.js.map +1 -0
- package/dist/src/main.d.ts +12 -0
- package/dist/src/main.js +59 -0
- package/dist/src/main.js.map +1 -0
- package/dist/src/socket-address-throttler.guard.d.ts +12 -0
- package/dist/src/socket-address-throttler.guard.js +10 -0
- package/dist/src/socket-address-throttler.guard.js.map +1 -0
- package/dist/src/validation.pipe.d.ts +8 -0
- package/dist/src/validation.pipe.js +20 -0
- package/dist/src/validation.pipe.js.map +1 -0
- package/dist/test/e2e/app.e2e.spec.d.ts +1 -0
- package/dist/test/e2e/app.e2e.spec.js +276 -0
- package/dist/test/e2e/app.e2e.spec.js.map +1 -0
- package/dist/test/unit/certificates.spec.d.ts +1 -0
- package/dist/test/unit/certificates.spec.js +46 -0
- package/dist/test/unit/certificates.spec.js.map +1 -0
- package/dist/test/unit/cli.spec.d.ts +1 -0
- package/dist/test/unit/cli.spec.js +100 -0
- package/dist/test/unit/cli.spec.js.map +1 -0
- package/dist/test/unit/config.service.spec.d.ts +1 -0
- package/dist/test/unit/config.service.spec.js +189 -0
- package/dist/test/unit/config.service.spec.js.map +1 -0
- package/dist/test/unit/cors.spec.d.ts +1 -0
- package/dist/test/unit/cors.spec.js +41 -0
- package/dist/test/unit/cors.spec.js.map +1 -0
- package/dist/test/unit/logger-auth.guard.spec.d.ts +1 -0
- package/dist/test/unit/logger-auth.guard.spec.js +40 -0
- package/dist/test/unit/logger-auth.guard.spec.js.map +1 -0
- package/dist/test/unit/logs.service.spec.d.ts +1 -0
- package/dist/test/unit/logs.service.spec.js +246 -0
- package/dist/test/unit/logs.service.spec.js.map +1 -0
- package/dist/test/unit/socket-address-throttler.guard.spec.d.ts +1 -0
- package/dist/test/unit/socket-address-throttler.guard.spec.js +18 -0
- package/dist/test/unit/socket-address-throttler.guard.spec.js.map +1 -0
- package/dist/test/unit/validation.pipe.spec.d.ts +1 -0
- package/dist/test/unit/validation.pipe.spec.js +132 -0
- package/dist/test/unit/validation.pipe.spec.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +80 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, jest, } from '@jest/globals';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import { Logger } from '@nestjs/common';
|
|
7
|
+
import LogsService from 'src/logs/logs.service';
|
|
8
|
+
import Config from 'src/config/config.service';
|
|
9
|
+
function installFakeStream(service, stream) {
|
|
10
|
+
const internals = service;
|
|
11
|
+
internals.initialized = true;
|
|
12
|
+
internals.writeStream = stream;
|
|
13
|
+
}
|
|
14
|
+
const noListener = () => undefined;
|
|
15
|
+
const baseEvent = {
|
|
16
|
+
sessionId: '4a4f6d5f-818d-4c86-b5dc-0d4f8a38dc02',
|
|
17
|
+
userHash: 'a3f2b8c1d4e5f67890abcdef1234567890abcdef1234567890abcdef12345678',
|
|
18
|
+
timestamp: '2026-03-24T20:00:00.000Z',
|
|
19
|
+
event: 'click',
|
|
20
|
+
page: '/insights/log',
|
|
21
|
+
element: 'button',
|
|
22
|
+
label: 'Refresh',
|
|
23
|
+
context: {
|
|
24
|
+
source: 'log-viewer',
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
const originalEnv = { ...process.env };
|
|
28
|
+
describe('LogsService', () => {
|
|
29
|
+
let tempDir = '';
|
|
30
|
+
let logFilePath = '';
|
|
31
|
+
beforeEach(async () => {
|
|
32
|
+
tempDir = await mkdtemp(path.join(os.tmpdir(), 'dtaas-logger-'));
|
|
33
|
+
logFilePath = path.join(tempDir, 'workflow.jsonl');
|
|
34
|
+
process.env = { ...originalEnv };
|
|
35
|
+
process.env.LOGGER_LOG_FILE_PATH = logFilePath;
|
|
36
|
+
});
|
|
37
|
+
afterEach(async () => {
|
|
38
|
+
jest.restoreAllMocks();
|
|
39
|
+
process.env = { ...originalEnv };
|
|
40
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
41
|
+
});
|
|
42
|
+
it('appends log events as jsonl', async () => {
|
|
43
|
+
const config = new Config();
|
|
44
|
+
const service = new LogsService(config);
|
|
45
|
+
await service.appendEvent(baseEvent);
|
|
46
|
+
await service.onModuleDestroy();
|
|
47
|
+
const content = await readFile(config.getLogFilePath(), 'utf8');
|
|
48
|
+
const lines = content.trim().split('\n');
|
|
49
|
+
expect(lines).toHaveLength(1);
|
|
50
|
+
expect(JSON.parse(lines[0])).toEqual(baseEvent);
|
|
51
|
+
});
|
|
52
|
+
it('repairs missing newline before append', async () => {
|
|
53
|
+
await writeFile(logFilePath, JSON.stringify(baseEvent), 'utf8');
|
|
54
|
+
const config = new Config();
|
|
55
|
+
const service = new LogsService(config);
|
|
56
|
+
const nextEvent = { ...baseEvent, label: 'Next action' };
|
|
57
|
+
await service.appendEvent(nextEvent);
|
|
58
|
+
await service.onModuleDestroy();
|
|
59
|
+
const content = await readFile(logFilePath, 'utf8');
|
|
60
|
+
const lines = content.trim().split('\n');
|
|
61
|
+
expect(lines).toHaveLength(2);
|
|
62
|
+
expect(JSON.parse(lines[0])).toEqual(baseEvent);
|
|
63
|
+
expect(JSON.parse(lines[1])).toEqual(nextEvent);
|
|
64
|
+
});
|
|
65
|
+
it('rotates the log when the configured size is exceeded', async () => {
|
|
66
|
+
process.env.LOGGER_LOG_MAX_BYTES = '1';
|
|
67
|
+
process.env.LOGGER_LOG_RETENTION_FILES = '2';
|
|
68
|
+
const config = new Config();
|
|
69
|
+
const service = new LogsService(config);
|
|
70
|
+
const nextEvent = { ...baseEvent, label: 'Next action' };
|
|
71
|
+
await service.appendEvent(baseEvent);
|
|
72
|
+
await service.appendEvent(nextEvent);
|
|
73
|
+
await service.onModuleDestroy();
|
|
74
|
+
const rotated = await readFile(`${logFilePath}.1`, 'utf8');
|
|
75
|
+
const current = await readFile(logFilePath, 'utf8');
|
|
76
|
+
expect(JSON.parse(rotated)).toEqual(baseEvent);
|
|
77
|
+
expect(JSON.parse(current)).toEqual(nextEvent);
|
|
78
|
+
});
|
|
79
|
+
it('appends multiple events in order', async () => {
|
|
80
|
+
const config = new Config();
|
|
81
|
+
const service = new LogsService(config);
|
|
82
|
+
const nextEvent = { ...baseEvent, label: 'Second action' };
|
|
83
|
+
await service.appendEvent(baseEvent);
|
|
84
|
+
await service.appendEvent(nextEvent);
|
|
85
|
+
await service.onModuleDestroy();
|
|
86
|
+
const content = await readFile(logFilePath, 'utf8');
|
|
87
|
+
const lines = content.trim().split('\n');
|
|
88
|
+
expect(lines).toHaveLength(2);
|
|
89
|
+
expect(JSON.parse(lines[0])).toEqual(baseEvent);
|
|
90
|
+
expect(JSON.parse(lines[1])).toEqual(nextEvent);
|
|
91
|
+
});
|
|
92
|
+
it('shares one initialization between concurrent appends', async () => {
|
|
93
|
+
const config = new Config();
|
|
94
|
+
const service = new LogsService(config);
|
|
95
|
+
const nextEvent = { ...baseEvent, label: 'Second action' };
|
|
96
|
+
const firstWrite = service.appendEvent(baseEvent);
|
|
97
|
+
const secondWrite = service.appendEvent(nextEvent);
|
|
98
|
+
await Promise.all([firstWrite, secondWrite]);
|
|
99
|
+
await service.onModuleDestroy();
|
|
100
|
+
const content = await readFile(logFilePath, 'utf8');
|
|
101
|
+
const lines = content.trim().split('\n');
|
|
102
|
+
expect(lines).toHaveLength(2);
|
|
103
|
+
expect(JSON.parse(lines[0])).toEqual(baseEvent);
|
|
104
|
+
expect(JSON.parse(lines[1])).toEqual(nextEvent);
|
|
105
|
+
});
|
|
106
|
+
it('skips newline repair for an empty log file', async () => {
|
|
107
|
+
await writeFile(logFilePath, '', 'utf8');
|
|
108
|
+
const config = new Config();
|
|
109
|
+
const service = new LogsService(config);
|
|
110
|
+
await service.appendEvent(baseEvent);
|
|
111
|
+
await service.onModuleDestroy();
|
|
112
|
+
const content = await readFile(logFilePath, 'utf8');
|
|
113
|
+
const lines = content.trim().split('\n');
|
|
114
|
+
expect(lines).toHaveLength(1);
|
|
115
|
+
expect(JSON.parse(lines[0])).toEqual(baseEvent);
|
|
116
|
+
});
|
|
117
|
+
it('keeps the file intact when it already ends with a newline', async () => {
|
|
118
|
+
await writeFile(logFilePath, `${JSON.stringify(baseEvent)}\n`, 'utf8');
|
|
119
|
+
const config = new Config();
|
|
120
|
+
const service = new LogsService(config);
|
|
121
|
+
const nextEvent = { ...baseEvent, label: 'Next action' };
|
|
122
|
+
await service.appendEvent(nextEvent);
|
|
123
|
+
await service.onModuleDestroy();
|
|
124
|
+
const content = await readFile(logFilePath, 'utf8');
|
|
125
|
+
const lines = content.trim().split('\n');
|
|
126
|
+
expect(lines).toHaveLength(2);
|
|
127
|
+
expect(JSON.parse(lines[0])).toEqual(baseEvent);
|
|
128
|
+
expect(JSON.parse(lines[1])).toEqual(nextEvent);
|
|
129
|
+
});
|
|
130
|
+
it('closes without a stream when no event was appended', async () => {
|
|
131
|
+
const config = new Config();
|
|
132
|
+
const service = new LogsService(config);
|
|
133
|
+
await service.onModuleDestroy();
|
|
134
|
+
expect(existsSync(logFilePath)).toBe(false);
|
|
135
|
+
});
|
|
136
|
+
it('rejects append when the stream reports a write error', async () => {
|
|
137
|
+
const errorSpy = jest
|
|
138
|
+
.spyOn(Logger.prototype, 'error')
|
|
139
|
+
.mockImplementation(() => undefined);
|
|
140
|
+
const service = new LogsService(new Config());
|
|
141
|
+
installFakeStream(service, {
|
|
142
|
+
on: noListener,
|
|
143
|
+
off: noListener,
|
|
144
|
+
write: (_line, _encoding, callback) => callback(new Error('disk full')),
|
|
145
|
+
end: (callback) => callback(null),
|
|
146
|
+
destroy: jest.fn(),
|
|
147
|
+
});
|
|
148
|
+
await expect(service.appendEvent(baseEvent)).rejects.toThrow('disk full');
|
|
149
|
+
expect(errorSpy).toHaveBeenCalled();
|
|
150
|
+
});
|
|
151
|
+
it('rejects append when the stream emits an error event', async () => {
|
|
152
|
+
const errorSpy = jest
|
|
153
|
+
.spyOn(Logger.prototype, 'error')
|
|
154
|
+
.mockImplementation(() => undefined);
|
|
155
|
+
const service = new LogsService(new Config());
|
|
156
|
+
let errorListener;
|
|
157
|
+
installFakeStream(service, {
|
|
158
|
+
on: (_event, listener) => {
|
|
159
|
+
errorListener = listener;
|
|
160
|
+
},
|
|
161
|
+
off: () => {
|
|
162
|
+
errorListener = undefined;
|
|
163
|
+
},
|
|
164
|
+
write: () => {
|
|
165
|
+
errorListener?.(new Error('stream failure'));
|
|
166
|
+
},
|
|
167
|
+
end: (callback) => callback(null),
|
|
168
|
+
destroy: jest.fn(),
|
|
169
|
+
});
|
|
170
|
+
await expect(service.appendEvent(baseEvent)).rejects.toThrow('stream failure');
|
|
171
|
+
expect(errorSpy).toHaveBeenCalled();
|
|
172
|
+
});
|
|
173
|
+
it('propagates close errors from the write stream', async () => {
|
|
174
|
+
const service = new LogsService(new Config());
|
|
175
|
+
installFakeStream(service, {
|
|
176
|
+
on: noListener,
|
|
177
|
+
off: noListener,
|
|
178
|
+
write: (_line, _encoding, callback) => callback(undefined),
|
|
179
|
+
end: (callback) => callback(new Error('close failed')),
|
|
180
|
+
destroy: jest.fn(),
|
|
181
|
+
});
|
|
182
|
+
await service.appendEvent(baseEvent);
|
|
183
|
+
await expect(service.onModuleDestroy()).rejects.toThrow('close failed');
|
|
184
|
+
});
|
|
185
|
+
it('resolves close when the stream reports a null error', async () => {
|
|
186
|
+
const service = new LogsService(new Config());
|
|
187
|
+
installFakeStream(service, {
|
|
188
|
+
on: noListener,
|
|
189
|
+
off: noListener,
|
|
190
|
+
write: (_line, _encoding, callback) => callback(undefined),
|
|
191
|
+
end: (callback) => callback(null),
|
|
192
|
+
destroy: jest.fn(),
|
|
193
|
+
});
|
|
194
|
+
await service.appendEvent(baseEvent);
|
|
195
|
+
await expect(service.onModuleDestroy()).resolves.toBeUndefined();
|
|
196
|
+
});
|
|
197
|
+
it('reopens the stream after a prior append failed', async () => {
|
|
198
|
+
const errorSpy = jest
|
|
199
|
+
.spyOn(Logger.prototype, 'error')
|
|
200
|
+
.mockImplementation(() => undefined);
|
|
201
|
+
const service = new LogsService(new Config());
|
|
202
|
+
const nextEvent = { ...baseEvent, label: 'Next action' };
|
|
203
|
+
const writtenLines = [];
|
|
204
|
+
let failNextWrite = true;
|
|
205
|
+
installFakeStream(service, {
|
|
206
|
+
on: noListener,
|
|
207
|
+
off: noListener,
|
|
208
|
+
write: (line, _encoding, callback) => {
|
|
209
|
+
if (failNextWrite) {
|
|
210
|
+
failNextWrite = false;
|
|
211
|
+
callback(new Error('disk full'));
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
writtenLines.push(line);
|
|
215
|
+
callback(undefined);
|
|
216
|
+
},
|
|
217
|
+
end: (callback) => callback(null),
|
|
218
|
+
destroy: jest.fn(),
|
|
219
|
+
});
|
|
220
|
+
await expect(service.appendEvent(baseEvent)).rejects.toThrow('disk full');
|
|
221
|
+
await service.appendEvent(nextEvent);
|
|
222
|
+
await service.onModuleDestroy();
|
|
223
|
+
const content = await readFile(logFilePath, 'utf8');
|
|
224
|
+
expect(JSON.parse(content.trim())).toEqual(nextEvent);
|
|
225
|
+
expect(writtenLines).toHaveLength(0);
|
|
226
|
+
expect(errorSpy).toHaveBeenCalled();
|
|
227
|
+
});
|
|
228
|
+
it('handles late stream errors without an active write listener', async () => {
|
|
229
|
+
const errorSpy = jest
|
|
230
|
+
.spyOn(Logger.prototype, 'error')
|
|
231
|
+
.mockImplementation(() => undefined);
|
|
232
|
+
const service = new LogsService(new Config());
|
|
233
|
+
await service.appendEvent(baseEvent);
|
|
234
|
+
const internals = service;
|
|
235
|
+
internals.writeStream?.emit('error', new Error('late stream failure'));
|
|
236
|
+
expect(errorSpy).toHaveBeenCalled();
|
|
237
|
+
expect(internals.initialized).toBe(false);
|
|
238
|
+
});
|
|
239
|
+
it('rejects append when the stream is missing after initialization', async () => {
|
|
240
|
+
const service = new LogsService(new Config());
|
|
241
|
+
const internals = service;
|
|
242
|
+
internals.initialized = true;
|
|
243
|
+
await expect(service.appendEvent(baseEvent)).rejects.toThrow('Logger write stream is not initialized');
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
//# sourceMappingURL=logs.service.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logs.service.spec.js","sourceRoot":"","sources":["../../../test/unit/logs.service.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,EAAE,EACF,MAAM,EACN,UAAU,EACV,SAAS,EACT,IAAI,GACL,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,WAAW,MAAM,uBAAuB,CAAC;AAChD,OAAO,MAAM,MAAM,2BAA2B,CAAC;AAa/C,SAAS,iBAAiB,CAAC,OAAoB,EAAE,MAAkB;IACjE,MAAM,SAAS,GAAG,OAGjB,CAAC;IACF,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC;IAC7B,SAAS,CAAC,WAAW,GAAG,MAAM,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,GAAG,GAAS,EAAE,CAAC,SAAS,CAAC;AAEzC,MAAM,SAAS,GAAgB;IAC7B,SAAS,EAAE,sCAAsC;IACjD,QAAQ,EAAE,kEAAkE;IAC5E,SAAS,EAAE,0BAA0B;IACrC,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,eAAe;IACrB,OAAO,EAAE,QAAQ;IACjB,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE;QACP,MAAM,EAAE,YAAY;KACrB;CACF,CAAC;AAEF,MAAM,WAAW,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;AAEvC,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,WAAW,GAAG,EAAE,CAAC;IAErB,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC;QACjE,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;QACnD,OAAO,CAAC,GAAG,GAAG,EAAE,GAAG,WAAW,EAAE,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,oBAAoB,GAAG,WAAW,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,GAAG,EAAE,GAAG,WAAW,EAAE,CAAC;QACjC,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;QAC3C,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;QAExC,MAAM,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC;QAEhC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,MAAM,CAAC,CAAC;QAChE,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAgB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC;QAChE,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,SAAS,GAAG,EAAE,GAAG,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAEzD,MAAM,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC;QAEhC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QACpD,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAgB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC/D,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAgB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;QACpE,OAAO,CAAC,GAAG,CAAC,oBAAoB,GAAG,GAAG,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,0BAA0B,GAAG,GAAG,CAAC;QAC7C,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,SAAS,GAAG,EAAE,GAAG,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAEzD,MAAM,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC;QAEhC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,GAAG,WAAW,IAAI,EAAE,MAAM,CAAC,CAAC;QAC3D,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QACpD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAgB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC9D,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAgB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;QAChD,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,SAAS,GAAG,EAAE,GAAG,SAAS,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC;QAE3D,MAAM,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC;QAEhC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QACpD,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAgB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC/D,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAgB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;QACpE,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,SAAS,GAAG,EAAE,GAAG,SAAS,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC;QAE3D,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QAClD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACnD,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;QAC7C,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC;QAEhC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QACpD,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAgB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC/D,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAgB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,SAAS,CAAC,WAAW,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;QAExC,MAAM,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC;QAEhC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QACpD,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAgB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QACzE,MAAM,SAAS,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACvE,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,SAAS,GAAG,EAAE,GAAG,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAEzD,MAAM,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC;QAEhC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QACpD,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAgB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC/D,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAgB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;QAExC,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC;QAEhC,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;QACpE,MAAM,QAAQ,GAAG,IAAI;aAClB,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC;aAChC,kBAAkB,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACvC,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;QAC9C,iBAAiB,CAAC,OAAO,EAAE;YACzB,EAAE,EAAE,UAAU;YACd,GAAG,EAAE,UAAU;YACf,KAAK,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC;YACvE,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC;YACjC,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE;SACnB,CAAC,CAAC;QAEH,MAAM,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAC1E,MAAM,CAAC,QAAQ,CAAC,CAAC,gBAAgB,EAAE,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACnE,MAAM,QAAQ,GAAG,IAAI;aAClB,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC;aAChC,kBAAkB,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACvC,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;QAC9C,IAAI,aAAmD,CAAC;QACxD,iBAAiB,CAAC,OAAO,EAAE;YACzB,EAAE,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE;gBACvB,aAAa,GAAG,QAAQ,CAAC;YAC3B,CAAC;YACD,GAAG,EAAE,GAAG,EAAE;gBACR,aAAa,GAAG,SAAS,CAAC;YAC5B,CAAC;YACD,KAAK,EAAE,GAAG,EAAE;gBACV,aAAa,EAAE,CAAC,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC;YAC/C,CAAC;YACD,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC;YACjC,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE;SACnB,CAAC,CAAC;QAEH,MAAM,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAC1D,gBAAgB,CACjB,CAAC;QACF,MAAM,CAAC,QAAQ,CAAC,CAAC,gBAAgB,EAAE,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;QAC7D,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;QAC9C,iBAAiB,CAAC,OAAO,EAAE;YACzB,EAAE,EAAE,UAAU;YACd,GAAG,EAAE,UAAU;YACf,KAAK,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC;YAC1D,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC;YACtD,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE;SACnB,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QAErC,MAAM,MAAM,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACnE,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;QAC9C,iBAAiB,CAAC,OAAO,EAAE;YACzB,EAAE,EAAE,UAAU;YACd,GAAG,EAAE,UAAU;YACf,KAAK,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC;YAC1D,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC;YACjC,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE;SACnB,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QAErC,MAAM,MAAM,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,QAAQ,GAAG,IAAI;aAClB,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC;aAChC,kBAAkB,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACvC,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;QAC9C,MAAM,SAAS,GAAG,EAAE,GAAG,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QACzD,MAAM,YAAY,GAAa,EAAE,CAAC;QAClC,IAAI,aAAa,GAAG,IAAI,CAAC;QACzB,iBAAiB,CAAC,OAAO,EAAE;YACzB,EAAE,EAAE,UAAU;YACd,GAAG,EAAE,UAAU;YACf,KAAK,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE;gBACnC,IAAI,aAAa,EAAE,CAAC;oBAClB,aAAa,GAAG,KAAK,CAAC;oBACtB,QAAQ,CAAC,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;oBACjC,OAAO;gBACT,CAAC;gBACD,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACxB,QAAQ,CAAC,SAAS,CAAC,CAAC;YACtB,CAAC;YACD,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC;YACjC,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE;SACnB,CAAC,CAAC;QAEH,MAAM,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAC1E,MAAM,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QAErC,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC;QAEhC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QACpD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAgB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACrE,MAAM,CAAC,YAAY,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACrC,MAAM,CAAC,QAAQ,CAAC,CAAC,gBAAgB,EAAE,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,QAAQ,GAAG,IAAI;aAClB,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC;aAChC,kBAAkB,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACvC,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;QAC9C,MAAM,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,SAAS,GAAG,OAGjB,CAAC;QAEF,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC,CAAC;QAEvE,MAAM,CAAC,QAAQ,CAAC,CAAC,gBAAgB,EAAE,CAAC;QACpC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;QAC9E,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;QAC9C,MAAM,SAAS,GAAG,OAA8C,CAAC;QACjE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC;QAE7B,MAAM,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAC1D,wCAAwC,CACzC,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, expect, it } from '@jest/globals';
|
|
2
|
+
import { getSocketTracker } from 'src/socket-address-throttler.guard';
|
|
3
|
+
describe('SocketAddressThrottlerGuard', () => {
|
|
4
|
+
it('tracks the actual socket address', () => {
|
|
5
|
+
expect(getSocketTracker({ socket: { remoteAddress: '172.20.0.5' } })).toBe('172.20.0.5');
|
|
6
|
+
});
|
|
7
|
+
it('ignores spoofable forwarded request fields', () => {
|
|
8
|
+
expect(getSocketTracker({
|
|
9
|
+
socket: { remoteAddress: '172.20.0.5' },
|
|
10
|
+
ip: '198.51.100.10',
|
|
11
|
+
ips: ['203.0.113.10'],
|
|
12
|
+
})).toBe('172.20.0.5');
|
|
13
|
+
});
|
|
14
|
+
it('falls back when the socket address is missing', () => {
|
|
15
|
+
expect(getSocketTracker({})).toBe('unknown');
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
//# sourceMappingURL=socket-address-throttler.guard.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket-address-throttler.guard.spec.js","sourceRoot":"","sources":["../../../test/unit/socket-address-throttler.guard.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAEtE,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;IAC3C,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,CAAC,gBAAgB,CAAC,EAAE,MAAM,EAAE,EAAE,aAAa,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CACxE,YAAY,CACb,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,CACJ,gBAAgB,CAAC;YACf,MAAM,EAAE,EAAE,aAAa,EAAE,YAAY,EAAE;YACvC,EAAE,EAAE,eAAe;YACnB,GAAG,EAAE,CAAC,cAAc,CAAC;SACtB,CAAC,CACH,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { describe, it, expect } from '@jest/globals';
|
|
2
|
+
import { ZodError } from 'zod';
|
|
3
|
+
import ZodValidationPipe from 'src/validation.pipe';
|
|
4
|
+
import { logEventSchema } from 'src/dto/log-event.dto';
|
|
5
|
+
function validPayload(context) {
|
|
6
|
+
return {
|
|
7
|
+
sessionId: '4a4f6d5f-818d-4c86-b5dc-0d4f8a38dc02',
|
|
8
|
+
userHash: 'a3f2b8c1d4e5f67890abcdef1234567890abcdef1234567890abcdef12345678',
|
|
9
|
+
timestamp: '2026-03-24T20:00:00.000Z',
|
|
10
|
+
event: 'click',
|
|
11
|
+
page: '/library',
|
|
12
|
+
element: 'tab',
|
|
13
|
+
label: 'Functions',
|
|
14
|
+
context,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function nestedContext(depth) {
|
|
18
|
+
let context = 'hidden';
|
|
19
|
+
for (let index = 0; index < depth; index += 1) {
|
|
20
|
+
context = { child: context };
|
|
21
|
+
}
|
|
22
|
+
return context;
|
|
23
|
+
}
|
|
24
|
+
describe('Log event validation pipe', () => {
|
|
25
|
+
it('validates a correct log payload', () => {
|
|
26
|
+
const payload = {
|
|
27
|
+
sessionId: '4a4f6d5f-818d-4c86-b5dc-0d4f8a38dc02',
|
|
28
|
+
userHash: 'a3f2b8c1d4e5f67890abcdef1234567890abcdef1234567890abcdef12345678',
|
|
29
|
+
timestamp: '2026-03-24T20:00:00.000Z',
|
|
30
|
+
event: 'click',
|
|
31
|
+
page: '/library',
|
|
32
|
+
element: 'button',
|
|
33
|
+
label: 'Start',
|
|
34
|
+
context: {
|
|
35
|
+
dt: {
|
|
36
|
+
name: 'WaterTank',
|
|
37
|
+
button: 'start',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
const validator = new ZodValidationPipe(logEventSchema);
|
|
42
|
+
expect(validator.transform(payload).page).toBe('/library');
|
|
43
|
+
});
|
|
44
|
+
it('validates a nested context payload', () => {
|
|
45
|
+
const payload = {
|
|
46
|
+
sessionId: '4a4f6d5f-818d-4c86-b5dc-0d4f8a38dc02',
|
|
47
|
+
userHash: 'a3f2b8c1d4e5f67890abcdef1234567890abcdef1234567890abcdef12345678',
|
|
48
|
+
timestamp: '2026-03-24T20:00:00.000Z',
|
|
49
|
+
event: 'click',
|
|
50
|
+
page: '/preview/digitaltwins',
|
|
51
|
+
element: 'button',
|
|
52
|
+
label: 'Start',
|
|
53
|
+
context: {
|
|
54
|
+
dt: {
|
|
55
|
+
name: 'hello',
|
|
56
|
+
button: 'start',
|
|
57
|
+
history: ['2026-03-24T19:00:00.000Z'],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
const validator = new ZodValidationPipe(logEventSchema);
|
|
62
|
+
expect(validator.transform(payload).context).toEqual(payload.context);
|
|
63
|
+
});
|
|
64
|
+
it('preserves navigation page transition metadata', () => {
|
|
65
|
+
const payload = {
|
|
66
|
+
...validPayload({ trigger: 'sidebar' }),
|
|
67
|
+
event: 'navigation',
|
|
68
|
+
page_transition: {
|
|
69
|
+
src: '/library',
|
|
70
|
+
target: '/preview/digitaltwins',
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
expect(logEventSchema.parse(payload).page_transition).toEqual(payload.page_transition);
|
|
74
|
+
});
|
|
75
|
+
it('defaults omitted context to an empty object', () => {
|
|
76
|
+
const payload = {
|
|
77
|
+
sessionId: '4a4f6d5f-818d-4c86-b5dc-0d4f8a38dc02',
|
|
78
|
+
userHash: 'a3f2b8c1d4e5f67890abcdef1234567890abcdef1234567890abcdef12345678',
|
|
79
|
+
timestamp: '2026-03-24T20:00:00.000Z',
|
|
80
|
+
event: 'click',
|
|
81
|
+
page: '/library',
|
|
82
|
+
element: 'tab',
|
|
83
|
+
label: 'Functions',
|
|
84
|
+
};
|
|
85
|
+
expect(logEventSchema.parse(payload).context).toEqual({});
|
|
86
|
+
});
|
|
87
|
+
it('rejects a context string value over the length limit', () => {
|
|
88
|
+
const payload = validPayload({ bad: 'a'.repeat(1025) });
|
|
89
|
+
expect(() => logEventSchema.parse(payload)).toThrow(ZodError);
|
|
90
|
+
});
|
|
91
|
+
it('rejects context deeper than the client context depth cap', () => {
|
|
92
|
+
const payload = validPayload(nestedContext(7));
|
|
93
|
+
expect(() => logEventSchema.parse(payload)).toThrow(ZodError);
|
|
94
|
+
});
|
|
95
|
+
it('rejects context over the total nested entry budget', () => {
|
|
96
|
+
const payload = validPayload({
|
|
97
|
+
values: Array.from({ length: 100 }, (_, index) => index),
|
|
98
|
+
});
|
|
99
|
+
expect(() => logEventSchema.parse(payload)).toThrow(ZodError);
|
|
100
|
+
});
|
|
101
|
+
it('rejects extremely deep context without a stack overflow', () => {
|
|
102
|
+
const payload = validPayload(nestedContext(2000));
|
|
103
|
+
expect(() => logEventSchema.parse(payload)).toThrow(ZodError);
|
|
104
|
+
});
|
|
105
|
+
it('schema rejects invalid payload', () => {
|
|
106
|
+
const invalidPayload = {
|
|
107
|
+
sessionId: 'not-a-uuid',
|
|
108
|
+
userHash: 'x',
|
|
109
|
+
timestamp: 'not-time',
|
|
110
|
+
event: 'click',
|
|
111
|
+
page: '/library',
|
|
112
|
+
element: 'tab',
|
|
113
|
+
label: 'Functions',
|
|
114
|
+
context: {},
|
|
115
|
+
};
|
|
116
|
+
expect(() => logEventSchema.parse(invalidPayload)).toThrow(ZodError);
|
|
117
|
+
});
|
|
118
|
+
it('rejects unknown event types', () => {
|
|
119
|
+
const payload = {
|
|
120
|
+
sessionId: '4a4f6d5f-818d-4c86-b5dc-0d4f8a38dc02',
|
|
121
|
+
userHash: 'a3f2b8c1d4e5f67890abcdef1234567890abcdef1234567890abcdef12345678',
|
|
122
|
+
timestamp: '2026-03-24T20:00:00.000Z',
|
|
123
|
+
event: 'submit',
|
|
124
|
+
page: '/library',
|
|
125
|
+
element: 'button',
|
|
126
|
+
label: 'Start',
|
|
127
|
+
context: {},
|
|
128
|
+
};
|
|
129
|
+
expect(() => logEventSchema.parse(payload)).toThrow(ZodError);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
//# sourceMappingURL=validation.pipe.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.pipe.spec.js","sourceRoot":"","sources":["../../../test/unit/validation.pipe.spec.ts"],"names":[],"mappings":"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"}
|