@logtape/syslog 1.2.2 → 1.2.4
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/package.json +5 -2
- package/deno.json +0 -34
- package/src/mod.ts +0 -15
- package/src/syslog.test.ts +0 -1475
- package/src/syslog.ts +0 -734
- package/tsdown.config.ts +0 -11
package/src/syslog.test.ts
DELETED
|
@@ -1,1475 +0,0 @@
|
|
|
1
|
-
import { suite } from "@alinea/suite";
|
|
2
|
-
import {
|
|
3
|
-
assert,
|
|
4
|
-
assertEquals,
|
|
5
|
-
assertInstanceOf,
|
|
6
|
-
assertRejects,
|
|
7
|
-
assertThrows,
|
|
8
|
-
} from "@std/assert";
|
|
9
|
-
import type { LogRecord } from "@logtape/logtape";
|
|
10
|
-
import { createSocket } from "node:dgram";
|
|
11
|
-
import { createServer } from "node:net";
|
|
12
|
-
import {
|
|
13
|
-
DenoTcpSyslogConnection,
|
|
14
|
-
DenoUdpSyslogConnection,
|
|
15
|
-
getSyslogSink,
|
|
16
|
-
NodeTcpSyslogConnection,
|
|
17
|
-
NodeUdpSyslogConnection,
|
|
18
|
-
type SyslogFacility,
|
|
19
|
-
} from "./syslog.ts";
|
|
20
|
-
|
|
21
|
-
const test = suite(import.meta);
|
|
22
|
-
|
|
23
|
-
// RFC 5424 syslog message parser for testing
|
|
24
|
-
interface ParsedSyslogMessage {
|
|
25
|
-
priority: number;
|
|
26
|
-
version: number;
|
|
27
|
-
timestamp: string;
|
|
28
|
-
hostname: string;
|
|
29
|
-
appName: string;
|
|
30
|
-
procId: string;
|
|
31
|
-
msgId: string;
|
|
32
|
-
structuredData: string;
|
|
33
|
-
message: string;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function parseSyslogMessage(rawMessage: string): ParsedSyslogMessage {
|
|
37
|
-
// RFC 5424 format: <PRI>VERSION TIMESTAMP HOSTNAME APP-NAME PROCID MSGID STRUCTURED-DATA MSG
|
|
38
|
-
const regex = /^<(\d+)>(\d+) (\S+) (\S+) (\S+) (\S+) (\S+) (.*)$/;
|
|
39
|
-
const match = rawMessage.match(regex);
|
|
40
|
-
|
|
41
|
-
if (!match) {
|
|
42
|
-
throw new Error(`Invalid syslog message format: ${rawMessage}`);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const remaining = match[8];
|
|
46
|
-
let structuredData: string;
|
|
47
|
-
let message: string;
|
|
48
|
-
|
|
49
|
-
if (remaining.startsWith("[")) {
|
|
50
|
-
// Parse structured data - need to handle escaped brackets properly
|
|
51
|
-
let pos = 0;
|
|
52
|
-
let bracketCount = 0;
|
|
53
|
-
let inQuotes = false;
|
|
54
|
-
let escaped = false;
|
|
55
|
-
|
|
56
|
-
for (let i = 0; i < remaining.length; i++) {
|
|
57
|
-
const char = remaining[i];
|
|
58
|
-
|
|
59
|
-
if (escaped) {
|
|
60
|
-
escaped = false;
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (char === "\\") {
|
|
65
|
-
escaped = true;
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (char === '"') {
|
|
70
|
-
inQuotes = !inQuotes;
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (!inQuotes) {
|
|
75
|
-
if (char === "[") {
|
|
76
|
-
bracketCount++;
|
|
77
|
-
} else if (char === "]") {
|
|
78
|
-
bracketCount--;
|
|
79
|
-
if (bracketCount === 0) {
|
|
80
|
-
// Found the end of structured data
|
|
81
|
-
pos = i + 1;
|
|
82
|
-
break;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (pos > 0 && pos < remaining.length && remaining[pos] === " ") {
|
|
89
|
-
structuredData = remaining.substring(0, pos);
|
|
90
|
-
message = remaining.substring(pos + 1);
|
|
91
|
-
} else {
|
|
92
|
-
// No message after structured data or structured data extends to end
|
|
93
|
-
structuredData = remaining.substring(0, pos || remaining.length);
|
|
94
|
-
message = "";
|
|
95
|
-
}
|
|
96
|
-
} else {
|
|
97
|
-
// No structured data, it's just "-"
|
|
98
|
-
const spaceIndex = remaining.indexOf(" ");
|
|
99
|
-
if (spaceIndex === -1) {
|
|
100
|
-
structuredData = remaining;
|
|
101
|
-
message = "";
|
|
102
|
-
} else {
|
|
103
|
-
structuredData = remaining.substring(0, spaceIndex);
|
|
104
|
-
message = remaining.substring(spaceIndex + 1);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return {
|
|
109
|
-
priority: parseInt(match[1]),
|
|
110
|
-
version: parseInt(match[2]),
|
|
111
|
-
timestamp: match[3],
|
|
112
|
-
hostname: match[4],
|
|
113
|
-
appName: match[5],
|
|
114
|
-
procId: match[6],
|
|
115
|
-
msgId: match[7],
|
|
116
|
-
structuredData,
|
|
117
|
-
message,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function parseStructuredData(
|
|
122
|
-
structuredDataStr: string,
|
|
123
|
-
): Record<string, string> {
|
|
124
|
-
if (structuredDataStr === "-") {
|
|
125
|
-
return {};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Parse [id key1="value1" key2="value2"] format
|
|
129
|
-
const match = structuredDataStr.match(/^\[([^\s]+)\s+(.*)\]$/);
|
|
130
|
-
if (!match) {
|
|
131
|
-
throw new Error(`Invalid structured data format: ${structuredDataStr}`);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const result: Record<string, string> = {};
|
|
135
|
-
const keyValuePairs = match[2];
|
|
136
|
-
|
|
137
|
-
// Parse key="value" pairs, handling escaped quotes
|
|
138
|
-
const kvRegex = /(\w+)="([^"\\]*(\\.[^"\\]*)*)"/g;
|
|
139
|
-
let kvMatch;
|
|
140
|
-
while ((kvMatch = kvRegex.exec(keyValuePairs)) !== null) {
|
|
141
|
-
const key = kvMatch[1];
|
|
142
|
-
const value = kvMatch[2]
|
|
143
|
-
.replace(/\\"/g, '"')
|
|
144
|
-
.replace(/\\\\/g, "\\")
|
|
145
|
-
.replace(/\\]/g, "]");
|
|
146
|
-
result[key] = value;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return result;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Create a mock log record for testing
|
|
153
|
-
function createMockLogRecord(
|
|
154
|
-
level: "trace" | "debug" | "info" | "warning" | "error" | "fatal" = "info",
|
|
155
|
-
message: (string | unknown)[] = ["Test message"],
|
|
156
|
-
properties?: Record<string, unknown>,
|
|
157
|
-
): LogRecord {
|
|
158
|
-
return {
|
|
159
|
-
category: ["test"],
|
|
160
|
-
level,
|
|
161
|
-
message,
|
|
162
|
-
rawMessage: "Test message",
|
|
163
|
-
timestamp: new Date("2024-01-01T12:00:00.000Z").getTime(),
|
|
164
|
-
properties: properties ?? {},
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
test("getSyslogSink() creates a sink function", () => {
|
|
169
|
-
const sink = getSyslogSink();
|
|
170
|
-
assertEquals(typeof sink, "function");
|
|
171
|
-
assertInstanceOf(sink, Function);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
test("getSyslogSink() creates an AsyncDisposable sink", () => {
|
|
175
|
-
const sink = getSyslogSink();
|
|
176
|
-
assertEquals(typeof sink[Symbol.asyncDispose], "function");
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
// Deno-specific UDP test
|
|
180
|
-
if (typeof Deno !== "undefined") {
|
|
181
|
-
test("getSyslogSink() with actual UDP message transmission (Deno)", async () => {
|
|
182
|
-
// For Deno, test UDP transmission to non-existent server (just verify no crash)
|
|
183
|
-
const sink = getSyslogSink({
|
|
184
|
-
hostname: "127.0.0.1",
|
|
185
|
-
port: 12345, // Use a fixed port that likely has no server
|
|
186
|
-
protocol: "udp",
|
|
187
|
-
facility: "local1",
|
|
188
|
-
appName: "test-app",
|
|
189
|
-
timeout: 100, // Short timeout
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
try {
|
|
193
|
-
// Send a log record - this should not crash even if server doesn't exist
|
|
194
|
-
const logRecord = createMockLogRecord("info", ["Test message"], {
|
|
195
|
-
userId: 123,
|
|
196
|
-
});
|
|
197
|
-
sink(logRecord);
|
|
198
|
-
|
|
199
|
-
// Wait for transmission attempt
|
|
200
|
-
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
201
|
-
|
|
202
|
-
// Test passes if no crash occurs
|
|
203
|
-
assertEquals(true, true);
|
|
204
|
-
} finally {
|
|
205
|
-
await sink[Symbol.asyncDispose]();
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
} else {
|
|
209
|
-
test("getSyslogSink() with actual UDP message transmission (Node.js)", async () => {
|
|
210
|
-
// Create a mock UDP server to receive messages
|
|
211
|
-
let receivedMessage = "";
|
|
212
|
-
|
|
213
|
-
// Node.js UDP server
|
|
214
|
-
const server = createSocket("udp4");
|
|
215
|
-
|
|
216
|
-
await new Promise<void>((resolve) => {
|
|
217
|
-
server.bind(0, "127.0.0.1", resolve);
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
const address = server.address() as { port: number };
|
|
221
|
-
|
|
222
|
-
server.on("message", (msg) => {
|
|
223
|
-
receivedMessage = msg.toString();
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
try {
|
|
227
|
-
// Create sink with UDP
|
|
228
|
-
const sink = getSyslogSink({
|
|
229
|
-
hostname: "127.0.0.1",
|
|
230
|
-
port: address.port,
|
|
231
|
-
protocol: "udp",
|
|
232
|
-
facility: "local1",
|
|
233
|
-
appName: "test-app",
|
|
234
|
-
timeout: 1000,
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
// Send a log record
|
|
238
|
-
const logRecord = createMockLogRecord("info", ["Test message"], {
|
|
239
|
-
userId: 123,
|
|
240
|
-
});
|
|
241
|
-
sink(logRecord);
|
|
242
|
-
|
|
243
|
-
// Wait for message transmission
|
|
244
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
245
|
-
await sink[Symbol.asyncDispose]();
|
|
246
|
-
|
|
247
|
-
// Wait for server to receive
|
|
248
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
249
|
-
|
|
250
|
-
// Verify the message format
|
|
251
|
-
assertEquals(receivedMessage.includes("Test message"), true);
|
|
252
|
-
assertEquals(receivedMessage.includes("test-app"), true);
|
|
253
|
-
// Priority should be local1 (17) * 8 + info (6) = 142
|
|
254
|
-
assertEquals(receivedMessage.includes("<142>1"), true);
|
|
255
|
-
} finally {
|
|
256
|
-
server.close();
|
|
257
|
-
}
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// Deno-specific TCP test
|
|
262
|
-
if (typeof Deno !== "undefined") {
|
|
263
|
-
test("getSyslogSink() with actual TCP message transmission (Deno)", async () => {
|
|
264
|
-
// Create a mock TCP server to receive messages
|
|
265
|
-
let receivedMessage = "";
|
|
266
|
-
|
|
267
|
-
// Deno TCP server
|
|
268
|
-
const server = Deno.listen({ port: 0 });
|
|
269
|
-
const serverAddr = server.addr as Deno.NetAddr;
|
|
270
|
-
|
|
271
|
-
const serverTask = (async () => {
|
|
272
|
-
try {
|
|
273
|
-
const conn = await server.accept();
|
|
274
|
-
const buffer = new Uint8Array(1024);
|
|
275
|
-
const bytesRead = await conn.read(buffer);
|
|
276
|
-
if (bytesRead) {
|
|
277
|
-
receivedMessage = new TextDecoder().decode(
|
|
278
|
-
buffer.subarray(0, bytesRead),
|
|
279
|
-
);
|
|
280
|
-
}
|
|
281
|
-
conn.close();
|
|
282
|
-
} catch {
|
|
283
|
-
// Server closed
|
|
284
|
-
}
|
|
285
|
-
})();
|
|
286
|
-
|
|
287
|
-
try {
|
|
288
|
-
// Create sink with TCP
|
|
289
|
-
const sink = getSyslogSink({
|
|
290
|
-
hostname: "127.0.0.1",
|
|
291
|
-
port: serverAddr.port,
|
|
292
|
-
protocol: "tcp",
|
|
293
|
-
facility: "daemon",
|
|
294
|
-
appName: "test-daemon",
|
|
295
|
-
timeout: 0, // No timeout to avoid timer leaks
|
|
296
|
-
includeStructuredData: true,
|
|
297
|
-
structuredDataId: "test@12345",
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
// Send a log record with properties
|
|
301
|
-
const logRecord = createMockLogRecord("error", [
|
|
302
|
-
"Critical error occurred",
|
|
303
|
-
], {
|
|
304
|
-
errorCode: 500,
|
|
305
|
-
component: "auth",
|
|
306
|
-
});
|
|
307
|
-
sink(logRecord);
|
|
308
|
-
|
|
309
|
-
// Wait for message transmission and disposal
|
|
310
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
311
|
-
await sink[Symbol.asyncDispose]();
|
|
312
|
-
|
|
313
|
-
// Wait for server task
|
|
314
|
-
await serverTask;
|
|
315
|
-
|
|
316
|
-
// Verify the message format
|
|
317
|
-
assertEquals(receivedMessage.includes("Critical error occurred"), true);
|
|
318
|
-
assertEquals(receivedMessage.includes("test-daemon"), true);
|
|
319
|
-
// Priority should be daemon (3) * 8 + error (3) = 27
|
|
320
|
-
assertEquals(receivedMessage.includes("<27>1"), true);
|
|
321
|
-
// Should include structured data
|
|
322
|
-
assertEquals(receivedMessage.includes("[test@12345"), true);
|
|
323
|
-
assertEquals(receivedMessage.includes('errorCode="500"'), true);
|
|
324
|
-
assertEquals(receivedMessage.includes('component="auth"'), true);
|
|
325
|
-
} finally {
|
|
326
|
-
server.close();
|
|
327
|
-
await serverTask.catch(() => {});
|
|
328
|
-
}
|
|
329
|
-
});
|
|
330
|
-
} else {
|
|
331
|
-
test("getSyslogSink() with actual TCP message transmission (Node.js)", async () => {
|
|
332
|
-
// Create a mock TCP server to receive messages
|
|
333
|
-
let receivedMessage = "";
|
|
334
|
-
|
|
335
|
-
// Node.js TCP server
|
|
336
|
-
const server = createServer((socket) => {
|
|
337
|
-
socket.on("data", (data) => {
|
|
338
|
-
receivedMessage = data.toString();
|
|
339
|
-
socket.end();
|
|
340
|
-
});
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
await new Promise<void>((resolve) => {
|
|
344
|
-
server.listen(0, "127.0.0.1", resolve);
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
const address = server.address() as { port: number };
|
|
348
|
-
|
|
349
|
-
try {
|
|
350
|
-
// Create sink with TCP
|
|
351
|
-
const sink = getSyslogSink({
|
|
352
|
-
hostname: "127.0.0.1",
|
|
353
|
-
port: address.port,
|
|
354
|
-
protocol: "tcp",
|
|
355
|
-
facility: "daemon",
|
|
356
|
-
appName: "test-daemon",
|
|
357
|
-
timeout: 5000,
|
|
358
|
-
includeStructuredData: true,
|
|
359
|
-
structuredDataId: "test@12345",
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
// Send a log record with properties
|
|
363
|
-
const logRecord = createMockLogRecord("error", [
|
|
364
|
-
"Critical error occurred",
|
|
365
|
-
], {
|
|
366
|
-
errorCode: 500,
|
|
367
|
-
component: "auth",
|
|
368
|
-
});
|
|
369
|
-
sink(logRecord);
|
|
370
|
-
|
|
371
|
-
// Wait for message transmission
|
|
372
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
373
|
-
await sink[Symbol.asyncDispose]();
|
|
374
|
-
|
|
375
|
-
// Wait for server to receive
|
|
376
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
377
|
-
|
|
378
|
-
// Verify the message format
|
|
379
|
-
assertEquals(receivedMessage.includes("Critical error occurred"), true);
|
|
380
|
-
assertEquals(receivedMessage.includes("test-daemon"), true);
|
|
381
|
-
// Priority should be daemon (3) * 8 + error (3) = 27
|
|
382
|
-
assertEquals(receivedMessage.includes("<27>1"), true);
|
|
383
|
-
// Should include structured data
|
|
384
|
-
assertEquals(receivedMessage.includes("[test@12345"), true);
|
|
385
|
-
assertEquals(receivedMessage.includes('errorCode="500"'), true);
|
|
386
|
-
assertEquals(receivedMessage.includes('component="auth"'), true);
|
|
387
|
-
} finally {
|
|
388
|
-
server.close();
|
|
389
|
-
}
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// Deno-specific multiple messages test
|
|
394
|
-
if (typeof Deno !== "undefined") {
|
|
395
|
-
test("getSyslogSink() with multiple messages and proper sequencing (Deno)", async () => {
|
|
396
|
-
// Test that multiple messages are sent in sequence without blocking
|
|
397
|
-
// Deno version - use UDP for simpler testing
|
|
398
|
-
const sink = getSyslogSink({
|
|
399
|
-
hostname: "127.0.0.1",
|
|
400
|
-
port: 1234, // Non-existent port, but should handle gracefully
|
|
401
|
-
protocol: "udp",
|
|
402
|
-
facility: "local0",
|
|
403
|
-
timeout: 100, // Short timeout
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
try {
|
|
407
|
-
// Send multiple messages quickly
|
|
408
|
-
const record1 = createMockLogRecord("info", ["Message 1"]);
|
|
409
|
-
const record2 = createMockLogRecord("warning", ["Message 2"]);
|
|
410
|
-
const record3 = createMockLogRecord("error", ["Message 3"]);
|
|
411
|
-
|
|
412
|
-
// These should not block each other
|
|
413
|
-
sink(record1);
|
|
414
|
-
sink(record2);
|
|
415
|
-
sink(record3);
|
|
416
|
-
|
|
417
|
-
// All messages should be queued and attempted
|
|
418
|
-
// Even if they fail due to no server, the sink should handle it gracefully
|
|
419
|
-
} finally {
|
|
420
|
-
await sink[Symbol.asyncDispose]();
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// Test passes if no hanging or crashes occur
|
|
424
|
-
assertEquals(true, true);
|
|
425
|
-
});
|
|
426
|
-
} else {
|
|
427
|
-
test("getSyslogSink() with multiple messages and proper sequencing (Node.js)", async () => {
|
|
428
|
-
// Test that multiple messages are sent in sequence without blocking
|
|
429
|
-
// Node.js version
|
|
430
|
-
const sink = getSyslogSink({
|
|
431
|
-
hostname: "127.0.0.1",
|
|
432
|
-
port: 1234, // Non-existent port
|
|
433
|
-
protocol: "udp",
|
|
434
|
-
facility: "local0",
|
|
435
|
-
timeout: 100,
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
try {
|
|
439
|
-
// Send multiple messages quickly
|
|
440
|
-
const record1 = createMockLogRecord("info", ["Message 1"]);
|
|
441
|
-
const record2 = createMockLogRecord("warning", ["Message 2"]);
|
|
442
|
-
const record3 = createMockLogRecord("error", ["Message 3"]);
|
|
443
|
-
|
|
444
|
-
sink(record1);
|
|
445
|
-
sink(record2);
|
|
446
|
-
sink(record3);
|
|
447
|
-
} finally {
|
|
448
|
-
await sink[Symbol.asyncDispose]();
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
assertEquals(true, true);
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
// RFC 5424 Message Format Validation Tests
|
|
456
|
-
if (typeof Deno !== "undefined") {
|
|
457
|
-
test("getSyslogSink() RFC 5424 message format validation (Deno)", async () => {
|
|
458
|
-
const receivedMessages: string[] = [];
|
|
459
|
-
|
|
460
|
-
// Use Node.js dgram API for UDP server (available in Deno)
|
|
461
|
-
const server = createSocket("udp4");
|
|
462
|
-
|
|
463
|
-
await new Promise<void>((resolve) => {
|
|
464
|
-
server.bind(0, "127.0.0.1", resolve);
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
const address = server.address() as { port: number };
|
|
468
|
-
|
|
469
|
-
server.on("message", (msg) => {
|
|
470
|
-
receivedMessages.push(msg.toString());
|
|
471
|
-
});
|
|
472
|
-
|
|
473
|
-
try {
|
|
474
|
-
const sink = getSyslogSink({
|
|
475
|
-
hostname: "127.0.0.1",
|
|
476
|
-
port: address.port,
|
|
477
|
-
protocol: "udp",
|
|
478
|
-
facility: "daemon", // 3
|
|
479
|
-
appName: "test-app",
|
|
480
|
-
syslogHostname: "test-host",
|
|
481
|
-
processId: "12345",
|
|
482
|
-
timeout: 1000,
|
|
483
|
-
includeStructuredData: true,
|
|
484
|
-
structuredDataId: "test@54321",
|
|
485
|
-
});
|
|
486
|
-
|
|
487
|
-
// Test different log levels with specific data
|
|
488
|
-
const infoRecord = createMockLogRecord("info", ["Info message"], {
|
|
489
|
-
requestId: "req-123",
|
|
490
|
-
userId: 456,
|
|
491
|
-
});
|
|
492
|
-
const errorRecord = createMockLogRecord("error", ["Error occurred"], {
|
|
493
|
-
errorCode: 500,
|
|
494
|
-
component: "auth",
|
|
495
|
-
});
|
|
496
|
-
const warningRecord = createMockLogRecord("warning", [
|
|
497
|
-
"Warning: disk space low",
|
|
498
|
-
], {
|
|
499
|
-
diskUsage: "85%",
|
|
500
|
-
partition: "/var",
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
sink(infoRecord);
|
|
504
|
-
sink(errorRecord);
|
|
505
|
-
sink(warningRecord);
|
|
506
|
-
|
|
507
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
508
|
-
await sink[Symbol.asyncDispose]();
|
|
509
|
-
|
|
510
|
-
// Wait for server to receive all messages
|
|
511
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
512
|
-
|
|
513
|
-
// Validate we received 3 messages
|
|
514
|
-
assertEquals(receivedMessages.length, 3);
|
|
515
|
-
|
|
516
|
-
// Parse and validate each message structure
|
|
517
|
-
const parsedMessages = receivedMessages.map((msg) =>
|
|
518
|
-
parseSyslogMessage(msg)
|
|
519
|
-
);
|
|
520
|
-
|
|
521
|
-
for (const parsed of parsedMessages) {
|
|
522
|
-
// Validate RFC 5424 format structure
|
|
523
|
-
assertEquals(parsed.version, 1);
|
|
524
|
-
assertEquals(parsed.hostname, "test-host");
|
|
525
|
-
assertEquals(parsed.appName, "test-app");
|
|
526
|
-
assertEquals(parsed.procId, "12345");
|
|
527
|
-
assertEquals(parsed.msgId, "-");
|
|
528
|
-
|
|
529
|
-
// Validate timestamp format (ISO 8601)
|
|
530
|
-
assert(
|
|
531
|
-
parsed.timestamp.match(
|
|
532
|
-
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/,
|
|
533
|
-
) !== null,
|
|
534
|
-
);
|
|
535
|
-
|
|
536
|
-
// Validate structured data is present
|
|
537
|
-
assert(parsed.structuredData.startsWith("[test@54321"));
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
// Find specific messages and validate their content
|
|
541
|
-
const infoMessage = parsedMessages.find((p) =>
|
|
542
|
-
p.message === "Info message"
|
|
543
|
-
);
|
|
544
|
-
const errorMessage = parsedMessages.find((p) =>
|
|
545
|
-
p.message === "Error occurred"
|
|
546
|
-
);
|
|
547
|
-
const warningMessage = parsedMessages.find((p) =>
|
|
548
|
-
p.message === "Warning: disk space low"
|
|
549
|
-
);
|
|
550
|
-
|
|
551
|
-
// Validate priorities: daemon (3) * 8 + severity
|
|
552
|
-
assertEquals(infoMessage?.priority, 30); // daemon (3) * 8 + info (6) = 30
|
|
553
|
-
assertEquals(errorMessage?.priority, 27); // daemon (3) * 8 + error (3) = 27
|
|
554
|
-
assertEquals(warningMessage?.priority, 28); // daemon (3) * 8 + warning (4) = 28
|
|
555
|
-
|
|
556
|
-
// Parse and validate structured data content
|
|
557
|
-
const infoStructuredData = parseStructuredData(
|
|
558
|
-
infoMessage!.structuredData,
|
|
559
|
-
);
|
|
560
|
-
assertEquals(infoStructuredData.requestId, "req-123");
|
|
561
|
-
assertEquals(infoStructuredData.userId, "456");
|
|
562
|
-
|
|
563
|
-
const errorStructuredData = parseStructuredData(
|
|
564
|
-
errorMessage!.structuredData,
|
|
565
|
-
);
|
|
566
|
-
assertEquals(errorStructuredData.errorCode, "500");
|
|
567
|
-
assertEquals(errorStructuredData.component, "auth");
|
|
568
|
-
|
|
569
|
-
const warningStructuredData = parseStructuredData(
|
|
570
|
-
warningMessage!.structuredData,
|
|
571
|
-
);
|
|
572
|
-
assertEquals(warningStructuredData.diskUsage, "85%");
|
|
573
|
-
assertEquals(warningStructuredData.partition, "/var");
|
|
574
|
-
} finally {
|
|
575
|
-
server.close();
|
|
576
|
-
}
|
|
577
|
-
});
|
|
578
|
-
} else {
|
|
579
|
-
test("getSyslogSink() RFC 5424 message format validation (Node.js)", async () => {
|
|
580
|
-
const receivedMessages: string[] = [];
|
|
581
|
-
|
|
582
|
-
// Create UDP server to capture actual messages
|
|
583
|
-
const server = createSocket("udp4");
|
|
584
|
-
|
|
585
|
-
await new Promise<void>((resolve) => {
|
|
586
|
-
server.bind(0, "127.0.0.1", resolve);
|
|
587
|
-
});
|
|
588
|
-
|
|
589
|
-
const address = server.address() as { port: number };
|
|
590
|
-
|
|
591
|
-
server.on("message", (msg) => {
|
|
592
|
-
receivedMessages.push(msg.toString());
|
|
593
|
-
});
|
|
594
|
-
|
|
595
|
-
try {
|
|
596
|
-
const sink = getSyslogSink({
|
|
597
|
-
hostname: "127.0.0.1",
|
|
598
|
-
port: address.port,
|
|
599
|
-
protocol: "udp",
|
|
600
|
-
facility: "daemon", // 3
|
|
601
|
-
appName: "test-app",
|
|
602
|
-
syslogHostname: "test-host",
|
|
603
|
-
processId: "12345",
|
|
604
|
-
timeout: 1000,
|
|
605
|
-
includeStructuredData: true,
|
|
606
|
-
structuredDataId: "test@54321",
|
|
607
|
-
});
|
|
608
|
-
|
|
609
|
-
// Test different log levels with specific data
|
|
610
|
-
const infoRecord = createMockLogRecord("info", ["Info message"], {
|
|
611
|
-
requestId: "req-123",
|
|
612
|
-
userId: 456,
|
|
613
|
-
});
|
|
614
|
-
const errorRecord = createMockLogRecord("error", ["Error occurred"], {
|
|
615
|
-
errorCode: 500,
|
|
616
|
-
component: "auth",
|
|
617
|
-
});
|
|
618
|
-
const warningRecord = createMockLogRecord("warning", [
|
|
619
|
-
"Warning: disk space low",
|
|
620
|
-
], {
|
|
621
|
-
diskUsage: "85%",
|
|
622
|
-
partition: "/var",
|
|
623
|
-
});
|
|
624
|
-
|
|
625
|
-
sink(infoRecord);
|
|
626
|
-
sink(errorRecord);
|
|
627
|
-
sink(warningRecord);
|
|
628
|
-
|
|
629
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
630
|
-
await sink[Symbol.asyncDispose]();
|
|
631
|
-
|
|
632
|
-
// Wait for server to receive all messages
|
|
633
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
634
|
-
|
|
635
|
-
// Validate we received 3 messages
|
|
636
|
-
assertEquals(receivedMessages.length, 3);
|
|
637
|
-
|
|
638
|
-
// Validate RFC 5424 format for each message
|
|
639
|
-
for (const message of receivedMessages) {
|
|
640
|
-
// Should start with priority in angle brackets
|
|
641
|
-
assertEquals(message.match(/^<\d+>/) !== null, true);
|
|
642
|
-
|
|
643
|
-
// Should have version number 1
|
|
644
|
-
assertEquals(message.includes(">1 "), true);
|
|
645
|
-
|
|
646
|
-
// Should contain timestamp in ISO format
|
|
647
|
-
assertEquals(
|
|
648
|
-
message.match(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/) !== null,
|
|
649
|
-
true,
|
|
650
|
-
);
|
|
651
|
-
|
|
652
|
-
// Should contain our hostname
|
|
653
|
-
assertEquals(message.includes("test-host"), true);
|
|
654
|
-
|
|
655
|
-
// Should contain our app name
|
|
656
|
-
assertEquals(message.includes("test-app"), true);
|
|
657
|
-
|
|
658
|
-
// Should contain our process ID
|
|
659
|
-
assertEquals(message.includes("12345"), true);
|
|
660
|
-
|
|
661
|
-
// Should contain structured data
|
|
662
|
-
assertEquals(message.includes("[test@54321"), true);
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
// Check specific priorities: daemon (3) * 8 + level
|
|
666
|
-
const infoMessage = receivedMessages.find((m) =>
|
|
667
|
-
m.includes("Info message")
|
|
668
|
-
);
|
|
669
|
-
const errorMessage = receivedMessages.find((m) =>
|
|
670
|
-
m.includes("Error occurred")
|
|
671
|
-
);
|
|
672
|
-
const warningMessage = receivedMessages.find((m) =>
|
|
673
|
-
m.includes("Warning: disk space low")
|
|
674
|
-
);
|
|
675
|
-
|
|
676
|
-
// Info: daemon (3) * 8 + info (6) = 30
|
|
677
|
-
assertEquals(infoMessage?.includes("<30>1"), true);
|
|
678
|
-
|
|
679
|
-
// Error: daemon (3) * 8 + error (3) = 27
|
|
680
|
-
assertEquals(errorMessage?.includes("<27>1"), true);
|
|
681
|
-
|
|
682
|
-
// Warning: daemon (3) * 8 + warning (4) = 28
|
|
683
|
-
assertEquals(warningMessage?.includes("<28>1"), true);
|
|
684
|
-
|
|
685
|
-
// Check structured data content
|
|
686
|
-
assertEquals(infoMessage?.includes('requestId="req-123"'), true);
|
|
687
|
-
assertEquals(infoMessage?.includes('userId="456"'), true);
|
|
688
|
-
assertEquals(errorMessage?.includes('errorCode="500"'), true);
|
|
689
|
-
assertEquals(errorMessage?.includes('component="auth"'), true);
|
|
690
|
-
assertEquals(warningMessage?.includes('diskUsage="85%"'), true);
|
|
691
|
-
assertEquals(warningMessage?.includes('partition="/var"'), true);
|
|
692
|
-
} finally {
|
|
693
|
-
server.close();
|
|
694
|
-
}
|
|
695
|
-
});
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
// Structured Data Escaping Tests
|
|
699
|
-
if (typeof Deno !== "undefined") {
|
|
700
|
-
test("getSyslogSink() structured data escaping validation (Deno)", async () => {
|
|
701
|
-
let receivedMessage = "";
|
|
702
|
-
|
|
703
|
-
const server = createSocket("udp4");
|
|
704
|
-
|
|
705
|
-
await new Promise<void>((resolve) => {
|
|
706
|
-
server.bind(0, "127.0.0.1", resolve);
|
|
707
|
-
});
|
|
708
|
-
|
|
709
|
-
const address = server.address() as { port: number };
|
|
710
|
-
|
|
711
|
-
server.on("message", (msg) => {
|
|
712
|
-
receivedMessage = msg.toString();
|
|
713
|
-
});
|
|
714
|
-
|
|
715
|
-
try {
|
|
716
|
-
const sink = getSyslogSink({
|
|
717
|
-
hostname: "127.0.0.1",
|
|
718
|
-
port: address.port,
|
|
719
|
-
protocol: "udp",
|
|
720
|
-
facility: "local0",
|
|
721
|
-
appName: "escape-test",
|
|
722
|
-
timeout: 1000,
|
|
723
|
-
includeStructuredData: true,
|
|
724
|
-
structuredDataId: "escape@12345",
|
|
725
|
-
});
|
|
726
|
-
|
|
727
|
-
// Test message with special characters that need escaping
|
|
728
|
-
const testRecord = createMockLogRecord("info", ["Test escaping"], {
|
|
729
|
-
quote: 'Has "quotes" in value',
|
|
730
|
-
backslash: "Has \\ backslash",
|
|
731
|
-
bracket: "Has ] bracket",
|
|
732
|
-
combined: 'Mix of "quotes", \\ and ] chars',
|
|
733
|
-
});
|
|
734
|
-
|
|
735
|
-
sink(testRecord);
|
|
736
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
737
|
-
await sink[Symbol.asyncDispose]();
|
|
738
|
-
|
|
739
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
740
|
-
|
|
741
|
-
// Parse and validate the complete message
|
|
742
|
-
const parsed = parseSyslogMessage(receivedMessage);
|
|
743
|
-
|
|
744
|
-
assertEquals(parsed.version, 1);
|
|
745
|
-
assertEquals(parsed.hostname, Deno.hostname());
|
|
746
|
-
assertEquals(parsed.appName, "escape-test");
|
|
747
|
-
assertEquals(parsed.message, "Test escaping");
|
|
748
|
-
|
|
749
|
-
// Parse structured data and verify proper unescaping
|
|
750
|
-
const structuredData = parseStructuredData(parsed.structuredData);
|
|
751
|
-
assertEquals(structuredData.quote, 'Has "quotes" in value');
|
|
752
|
-
assertEquals(structuredData.backslash, "Has \\ backslash");
|
|
753
|
-
assertEquals(structuredData.bracket, "Has ] bracket");
|
|
754
|
-
assertEquals(structuredData.combined, 'Mix of "quotes", \\ and ] chars');
|
|
755
|
-
} finally {
|
|
756
|
-
server.close();
|
|
757
|
-
}
|
|
758
|
-
});
|
|
759
|
-
} else {
|
|
760
|
-
test("getSyslogSink() structured data escaping validation (Node.js)", async () => {
|
|
761
|
-
let receivedMessage = "";
|
|
762
|
-
|
|
763
|
-
const server = createSocket("udp4");
|
|
764
|
-
|
|
765
|
-
await new Promise<void>((resolve) => {
|
|
766
|
-
server.bind(0, "127.0.0.1", resolve);
|
|
767
|
-
});
|
|
768
|
-
|
|
769
|
-
const address = server.address() as { port: number };
|
|
770
|
-
|
|
771
|
-
server.on("message", (msg) => {
|
|
772
|
-
receivedMessage = msg.toString();
|
|
773
|
-
});
|
|
774
|
-
|
|
775
|
-
try {
|
|
776
|
-
const sink = getSyslogSink({
|
|
777
|
-
hostname: "127.0.0.1",
|
|
778
|
-
port: address.port,
|
|
779
|
-
protocol: "udp",
|
|
780
|
-
facility: "local0",
|
|
781
|
-
appName: "escape-test",
|
|
782
|
-
timeout: 1000,
|
|
783
|
-
includeStructuredData: true,
|
|
784
|
-
structuredDataId: "escape@12345",
|
|
785
|
-
});
|
|
786
|
-
|
|
787
|
-
// Test message with special characters that need escaping
|
|
788
|
-
const testRecord = createMockLogRecord("info", ["Test escaping"], {
|
|
789
|
-
quote: 'Has "quotes" in value',
|
|
790
|
-
backslash: "Has \\ backslash",
|
|
791
|
-
bracket: "Has ] bracket",
|
|
792
|
-
combined: 'Mix of "quotes", \\ and ] chars',
|
|
793
|
-
});
|
|
794
|
-
|
|
795
|
-
sink(testRecord);
|
|
796
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
797
|
-
await sink[Symbol.asyncDispose]();
|
|
798
|
-
|
|
799
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
800
|
-
|
|
801
|
-
// Parse and verify the message like the Deno test
|
|
802
|
-
const parsed = parseSyslogMessage(receivedMessage);
|
|
803
|
-
|
|
804
|
-
assertEquals(parsed.version, 1);
|
|
805
|
-
assertEquals(parsed.message, "Test escaping");
|
|
806
|
-
assertEquals(parsed.appName, "escape-test");
|
|
807
|
-
|
|
808
|
-
// Parse structured data and verify escaping
|
|
809
|
-
const structuredData = parseStructuredData(parsed.structuredData);
|
|
810
|
-
assertEquals(structuredData.quote, 'Has "quotes" in value');
|
|
811
|
-
assertEquals(structuredData.backslash, "Has \\ backslash");
|
|
812
|
-
assertEquals(structuredData.bracket, "Has ] bracket");
|
|
813
|
-
assertEquals(structuredData.combined, 'Mix of "quotes", \\ and ] chars');
|
|
814
|
-
} finally {
|
|
815
|
-
server.close();
|
|
816
|
-
}
|
|
817
|
-
});
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
// Template Literal Message Formatting Tests
|
|
821
|
-
if (typeof Deno !== "undefined") {
|
|
822
|
-
test("getSyslogSink() template literal message formatting (Deno)", async () => {
|
|
823
|
-
let receivedMessage = "";
|
|
824
|
-
|
|
825
|
-
const server = createSocket("udp4");
|
|
826
|
-
|
|
827
|
-
await new Promise<void>((resolve) => {
|
|
828
|
-
server.bind(0, "127.0.0.1", resolve);
|
|
829
|
-
});
|
|
830
|
-
|
|
831
|
-
const address = server.address() as { port: number };
|
|
832
|
-
|
|
833
|
-
server.on("message", (msg) => {
|
|
834
|
-
receivedMessage = msg.toString();
|
|
835
|
-
});
|
|
836
|
-
|
|
837
|
-
try {
|
|
838
|
-
const sink = getSyslogSink({
|
|
839
|
-
hostname: "127.0.0.1",
|
|
840
|
-
port: address.port,
|
|
841
|
-
protocol: "udp",
|
|
842
|
-
facility: "local0",
|
|
843
|
-
appName: "template-test",
|
|
844
|
-
timeout: 1000,
|
|
845
|
-
includeStructuredData: false,
|
|
846
|
-
});
|
|
847
|
-
|
|
848
|
-
// Test LogTape template literal style message
|
|
849
|
-
const templateRecord = createMockLogRecord("info", [
|
|
850
|
-
"User ",
|
|
851
|
-
{ userId: 123, name: "Alice" },
|
|
852
|
-
" performed action ",
|
|
853
|
-
"login",
|
|
854
|
-
" with result ",
|
|
855
|
-
{ success: true, duration: 150 },
|
|
856
|
-
]);
|
|
857
|
-
|
|
858
|
-
sink(templateRecord);
|
|
859
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
860
|
-
await sink[Symbol.asyncDispose]();
|
|
861
|
-
|
|
862
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
863
|
-
|
|
864
|
-
// Parse and validate the complete message
|
|
865
|
-
const parsed = parseSyslogMessage(receivedMessage);
|
|
866
|
-
assertEquals(parsed.appName, "template-test");
|
|
867
|
-
assertEquals(parsed.structuredData, "-"); // No structured data for this test
|
|
868
|
-
|
|
869
|
-
// Verify template literal message is correctly formatted
|
|
870
|
-
const expectedMessage =
|
|
871
|
-
'User {"userId":123,"name":"Alice"} performed action "login" with result {"success":true,"duration":150}';
|
|
872
|
-
assertEquals(parsed.message, expectedMessage);
|
|
873
|
-
} finally {
|
|
874
|
-
server.close();
|
|
875
|
-
}
|
|
876
|
-
});
|
|
877
|
-
} else {
|
|
878
|
-
test("getSyslogSink() template literal message formatting (Node.js)", async () => {
|
|
879
|
-
let receivedMessage = "";
|
|
880
|
-
|
|
881
|
-
const server = createSocket("udp4");
|
|
882
|
-
|
|
883
|
-
await new Promise<void>((resolve) => {
|
|
884
|
-
server.bind(0, "127.0.0.1", resolve);
|
|
885
|
-
});
|
|
886
|
-
|
|
887
|
-
const address = server.address() as { port: number };
|
|
888
|
-
|
|
889
|
-
server.on("message", (msg) => {
|
|
890
|
-
receivedMessage = msg.toString();
|
|
891
|
-
});
|
|
892
|
-
|
|
893
|
-
try {
|
|
894
|
-
const sink = getSyslogSink({
|
|
895
|
-
hostname: "127.0.0.1",
|
|
896
|
-
port: address.port,
|
|
897
|
-
protocol: "udp",
|
|
898
|
-
facility: "local0",
|
|
899
|
-
appName: "template-test",
|
|
900
|
-
timeout: 1000,
|
|
901
|
-
includeStructuredData: false,
|
|
902
|
-
});
|
|
903
|
-
|
|
904
|
-
// Test LogTape template literal style message
|
|
905
|
-
const templateRecord = createMockLogRecord("info", [
|
|
906
|
-
"User ",
|
|
907
|
-
{ userId: 123, name: "Alice" },
|
|
908
|
-
" performed action ",
|
|
909
|
-
"login",
|
|
910
|
-
" with result ",
|
|
911
|
-
{ success: true, duration: 150 },
|
|
912
|
-
]);
|
|
913
|
-
|
|
914
|
-
sink(templateRecord);
|
|
915
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
916
|
-
await sink[Symbol.asyncDispose]();
|
|
917
|
-
|
|
918
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
919
|
-
|
|
920
|
-
// Verify template literal parts are properly formatted
|
|
921
|
-
assertEquals(receivedMessage.includes("User "), true);
|
|
922
|
-
assertEquals(
|
|
923
|
-
receivedMessage.includes('{"userId":123,"name":"Alice"}'),
|
|
924
|
-
true,
|
|
925
|
-
);
|
|
926
|
-
assertEquals(receivedMessage.includes(" performed action "), true);
|
|
927
|
-
assertEquals(receivedMessage.includes('"login"'), true);
|
|
928
|
-
assertEquals(receivedMessage.includes(" with result "), true);
|
|
929
|
-
assertEquals(
|
|
930
|
-
receivedMessage.includes('{"success":true,"duration":150}'),
|
|
931
|
-
true,
|
|
932
|
-
);
|
|
933
|
-
} finally {
|
|
934
|
-
server.close();
|
|
935
|
-
}
|
|
936
|
-
});
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
// Facility and Priority Calculation Tests
|
|
940
|
-
if (typeof Deno !== "undefined") {
|
|
941
|
-
test("getSyslogSink() facility and priority calculation (Deno)", async () => {
|
|
942
|
-
const receivedMessages: string[] = [];
|
|
943
|
-
|
|
944
|
-
const server = createSocket("udp4");
|
|
945
|
-
|
|
946
|
-
await new Promise<void>((resolve) => {
|
|
947
|
-
server.bind(0, "127.0.0.1", resolve);
|
|
948
|
-
});
|
|
949
|
-
|
|
950
|
-
const address = server.address() as { port: number };
|
|
951
|
-
|
|
952
|
-
server.on("message", (msg) => {
|
|
953
|
-
receivedMessages.push(msg.toString());
|
|
954
|
-
});
|
|
955
|
-
|
|
956
|
-
try {
|
|
957
|
-
// Test different facilities
|
|
958
|
-
const facilities = [
|
|
959
|
-
{ name: "kernel", code: 0 },
|
|
960
|
-
{ name: "user", code: 1 },
|
|
961
|
-
{ name: "daemon", code: 3 },
|
|
962
|
-
{ name: "local0", code: 16 },
|
|
963
|
-
{ name: "local7", code: 23 },
|
|
964
|
-
] as const;
|
|
965
|
-
|
|
966
|
-
for (const facility of facilities) {
|
|
967
|
-
const sink = getSyslogSink({
|
|
968
|
-
hostname: "127.0.0.1",
|
|
969
|
-
port: address.port,
|
|
970
|
-
protocol: "udp",
|
|
971
|
-
facility: facility.name,
|
|
972
|
-
appName: `${facility.name}-test`,
|
|
973
|
-
timeout: 1000,
|
|
974
|
-
});
|
|
975
|
-
|
|
976
|
-
// Test with error level (severity 3)
|
|
977
|
-
const errorRecord = createMockLogRecord("error", [
|
|
978
|
-
`${facility.name} error message`,
|
|
979
|
-
]);
|
|
980
|
-
sink(errorRecord);
|
|
981
|
-
|
|
982
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
983
|
-
await sink[Symbol.asyncDispose]();
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
// Test one additional level combination
|
|
987
|
-
const warningSink = getSyslogSink({
|
|
988
|
-
hostname: "127.0.0.1",
|
|
989
|
-
port: address.port,
|
|
990
|
-
protocol: "udp",
|
|
991
|
-
facility: "mail", // code 2
|
|
992
|
-
appName: "mail-test",
|
|
993
|
-
timeout: 1000,
|
|
994
|
-
});
|
|
995
|
-
|
|
996
|
-
const warningRecord = createMockLogRecord("warning", [
|
|
997
|
-
"mail warning message",
|
|
998
|
-
]);
|
|
999
|
-
warningSink(warningRecord);
|
|
1000
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
1001
|
-
await warningSink[Symbol.asyncDispose]();
|
|
1002
|
-
|
|
1003
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
1004
|
-
|
|
1005
|
-
// Verify priority calculations: Priority = Facility * 8 + Severity
|
|
1006
|
-
assertEquals(receivedMessages.length, 6);
|
|
1007
|
-
|
|
1008
|
-
// Parse all messages and verify priorities
|
|
1009
|
-
const parsedMessages = receivedMessages.map((msg) =>
|
|
1010
|
-
parseSyslogMessage(msg)
|
|
1011
|
-
);
|
|
1012
|
-
|
|
1013
|
-
// Find and verify each facility message
|
|
1014
|
-
const kernelMsg = parsedMessages.find((p) =>
|
|
1015
|
-
p.message === "kernel error message"
|
|
1016
|
-
);
|
|
1017
|
-
const userMsg = parsedMessages.find((p) =>
|
|
1018
|
-
p.message === "user error message"
|
|
1019
|
-
);
|
|
1020
|
-
const daemonMsg = parsedMessages.find((p) =>
|
|
1021
|
-
p.message === "daemon error message"
|
|
1022
|
-
);
|
|
1023
|
-
const local0Msg = parsedMessages.find((p) =>
|
|
1024
|
-
p.message === "local0 error message"
|
|
1025
|
-
);
|
|
1026
|
-
const local7Msg = parsedMessages.find((p) =>
|
|
1027
|
-
p.message === "local7 error message"
|
|
1028
|
-
);
|
|
1029
|
-
const mailMsg = parsedMessages.find((p) =>
|
|
1030
|
-
p.message === "mail warning message"
|
|
1031
|
-
);
|
|
1032
|
-
|
|
1033
|
-
// Verify exact priority calculations
|
|
1034
|
-
assertEquals(kernelMsg?.priority, 3); // kernel (0) * 8 + error (3) = 3
|
|
1035
|
-
assertEquals(userMsg?.priority, 11); // user (1) * 8 + error (3) = 11
|
|
1036
|
-
assertEquals(daemonMsg?.priority, 27); // daemon (3) * 8 + error (3) = 27
|
|
1037
|
-
assertEquals(local0Msg?.priority, 131); // local0 (16) * 8 + error (3) = 131
|
|
1038
|
-
assertEquals(local7Msg?.priority, 187); // local7 (23) * 8 + error (3) = 187
|
|
1039
|
-
assertEquals(mailMsg?.priority, 20); // mail (2) * 8 + warning (4) = 20
|
|
1040
|
-
|
|
1041
|
-
// Verify app names match facilities
|
|
1042
|
-
assertEquals(kernelMsg?.appName, "kernel-test");
|
|
1043
|
-
assertEquals(userMsg?.appName, "user-test");
|
|
1044
|
-
assertEquals(daemonMsg?.appName, "daemon-test");
|
|
1045
|
-
assertEquals(local0Msg?.appName, "local0-test");
|
|
1046
|
-
assertEquals(local7Msg?.appName, "local7-test");
|
|
1047
|
-
assertEquals(mailMsg?.appName, "mail-test");
|
|
1048
|
-
} finally {
|
|
1049
|
-
server.close();
|
|
1050
|
-
}
|
|
1051
|
-
});
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
test("Syslog message format follows RFC 5424", () => {
|
|
1055
|
-
// Test that the sink can be created and called without throwing
|
|
1056
|
-
const sink = getSyslogSink({
|
|
1057
|
-
facility: "local0",
|
|
1058
|
-
appName: "test-app",
|
|
1059
|
-
syslogHostname: "test-host",
|
|
1060
|
-
processId: "1234",
|
|
1061
|
-
includeStructuredData: false,
|
|
1062
|
-
});
|
|
1063
|
-
|
|
1064
|
-
// This should not throw during sink creation and call
|
|
1065
|
-
// We don't send the message to avoid network operations
|
|
1066
|
-
assertEquals(typeof sink, "function");
|
|
1067
|
-
assertEquals(typeof sink[Symbol.asyncDispose], "function");
|
|
1068
|
-
});
|
|
1069
|
-
|
|
1070
|
-
test("Syslog priority calculation", () => {
|
|
1071
|
-
// Test priority calculation: Priority = Facility * 8 + Severity
|
|
1072
|
-
// local0 (16) + info (6) = 16 * 8 + 6 = 134
|
|
1073
|
-
|
|
1074
|
-
const sink = getSyslogSink({
|
|
1075
|
-
facility: "local0", // 16
|
|
1076
|
-
appName: "test",
|
|
1077
|
-
});
|
|
1078
|
-
|
|
1079
|
-
// Test that sink is created correctly
|
|
1080
|
-
assertEquals(typeof sink, "function");
|
|
1081
|
-
assertEquals(typeof sink[Symbol.asyncDispose], "function");
|
|
1082
|
-
});
|
|
1083
|
-
|
|
1084
|
-
test("Syslog facility codes mapping", () => {
|
|
1085
|
-
const facilities = [
|
|
1086
|
-
"kernel", // 0
|
|
1087
|
-
"user", // 1
|
|
1088
|
-
"mail", // 2
|
|
1089
|
-
"daemon", // 3
|
|
1090
|
-
"local0", // 16
|
|
1091
|
-
"local7", // 23
|
|
1092
|
-
];
|
|
1093
|
-
|
|
1094
|
-
for (const facility of facilities) {
|
|
1095
|
-
const sink = getSyslogSink({
|
|
1096
|
-
facility: facility as SyslogFacility,
|
|
1097
|
-
appName: "test",
|
|
1098
|
-
});
|
|
1099
|
-
|
|
1100
|
-
// Should not throw for any valid facility
|
|
1101
|
-
assertEquals(typeof sink, "function");
|
|
1102
|
-
assertEquals(typeof sink[Symbol.asyncDispose], "function");
|
|
1103
|
-
}
|
|
1104
|
-
});
|
|
1105
|
-
|
|
1106
|
-
test("Syslog severity levels mapping", () => {
|
|
1107
|
-
// Test that the sink works with all severity levels
|
|
1108
|
-
const _levels: Array<
|
|
1109
|
-
"fatal" | "error" | "warning" | "info" | "debug" | "trace"
|
|
1110
|
-
> = [
|
|
1111
|
-
"fatal", // 0 (Emergency)
|
|
1112
|
-
"error", // 3 (Error)
|
|
1113
|
-
"warning", // 4 (Warning)
|
|
1114
|
-
"info", // 6 (Informational)
|
|
1115
|
-
"debug", // 7 (Debug)
|
|
1116
|
-
"trace", // 7 (Debug)
|
|
1117
|
-
];
|
|
1118
|
-
|
|
1119
|
-
const sink = getSyslogSink({
|
|
1120
|
-
facility: "local0",
|
|
1121
|
-
appName: "test",
|
|
1122
|
-
});
|
|
1123
|
-
|
|
1124
|
-
// Should work with all valid levels
|
|
1125
|
-
assertEquals(typeof sink, "function");
|
|
1126
|
-
assertEquals(typeof sink[Symbol.asyncDispose], "function");
|
|
1127
|
-
});
|
|
1128
|
-
|
|
1129
|
-
test("Structured data formatting", () => {
|
|
1130
|
-
const sink = getSyslogSink({
|
|
1131
|
-
facility: "local0",
|
|
1132
|
-
appName: "test",
|
|
1133
|
-
includeStructuredData: true,
|
|
1134
|
-
});
|
|
1135
|
-
|
|
1136
|
-
// Should not throw when including structured data
|
|
1137
|
-
assertEquals(typeof sink, "function");
|
|
1138
|
-
assertEquals(typeof sink[Symbol.asyncDispose], "function");
|
|
1139
|
-
});
|
|
1140
|
-
|
|
1141
|
-
test("Message with template literals", () => {
|
|
1142
|
-
const sink = getSyslogSink({
|
|
1143
|
-
facility: "local0",
|
|
1144
|
-
appName: "test",
|
|
1145
|
-
});
|
|
1146
|
-
|
|
1147
|
-
// Should not throw with template literal style messages
|
|
1148
|
-
assertEquals(typeof sink, "function");
|
|
1149
|
-
assertEquals(typeof sink[Symbol.asyncDispose], "function");
|
|
1150
|
-
});
|
|
1151
|
-
|
|
1152
|
-
test("Default options", () => {
|
|
1153
|
-
const sink = getSyslogSink();
|
|
1154
|
-
|
|
1155
|
-
// Should work with default options
|
|
1156
|
-
assertEquals(typeof sink, "function");
|
|
1157
|
-
assertEquals(typeof sink[Symbol.asyncDispose], "function");
|
|
1158
|
-
});
|
|
1159
|
-
|
|
1160
|
-
test("Custom options", () => {
|
|
1161
|
-
const sink = getSyslogSink({
|
|
1162
|
-
protocol: "tcp",
|
|
1163
|
-
facility: "mail",
|
|
1164
|
-
appName: "my-app",
|
|
1165
|
-
syslogHostname: "web-server-01",
|
|
1166
|
-
processId: "9999",
|
|
1167
|
-
timeout: 1000,
|
|
1168
|
-
includeStructuredData: true,
|
|
1169
|
-
});
|
|
1170
|
-
|
|
1171
|
-
// Should work with custom options
|
|
1172
|
-
assertEquals(typeof sink, "function");
|
|
1173
|
-
assertEquals(typeof sink[Symbol.asyncDispose], "function");
|
|
1174
|
-
});
|
|
1175
|
-
|
|
1176
|
-
test("AsyncDisposable cleanup", async () => {
|
|
1177
|
-
const sink = getSyslogSink();
|
|
1178
|
-
|
|
1179
|
-
// Send a message
|
|
1180
|
-
const record = createMockLogRecord();
|
|
1181
|
-
sink(record);
|
|
1182
|
-
|
|
1183
|
-
// Should be able to dispose without throwing
|
|
1184
|
-
await sink[Symbol.asyncDispose]();
|
|
1185
|
-
});
|
|
1186
|
-
|
|
1187
|
-
// Basic format validation test
|
|
1188
|
-
test("Syslog message format validation", () => {
|
|
1189
|
-
// Test the internal formatting functions directly
|
|
1190
|
-
const timestamp = new Date("2024-01-01T12:00:00.000Z").getTime();
|
|
1191
|
-
|
|
1192
|
-
// Test priority calculation: local0 (16) * 8 + info (6) = 134
|
|
1193
|
-
const expectedPriority = 16 * 8 + 6; // 134
|
|
1194
|
-
assertEquals(expectedPriority, 134);
|
|
1195
|
-
|
|
1196
|
-
// Test timestamp formatting
|
|
1197
|
-
const timestampStr = new Date(timestamp).toISOString();
|
|
1198
|
-
assertEquals(timestampStr, "2024-01-01T12:00:00.000Z");
|
|
1199
|
-
});
|
|
1200
|
-
|
|
1201
|
-
// Runtime-specific connection tests
|
|
1202
|
-
if (typeof Deno !== "undefined") {
|
|
1203
|
-
// Deno-specific tests
|
|
1204
|
-
test("DenoUdpSyslogConnection instantiation", () => {
|
|
1205
|
-
const connection = new DenoUdpSyslogConnection("localhost", 514, 5000);
|
|
1206
|
-
assertInstanceOf(connection, DenoUdpSyslogConnection);
|
|
1207
|
-
});
|
|
1208
|
-
|
|
1209
|
-
test("DenoUdpSyslogConnection connect and close", () => {
|
|
1210
|
-
const connection = new DenoUdpSyslogConnection("localhost", 514, 5000);
|
|
1211
|
-
// connect() should not throw for UDP
|
|
1212
|
-
connection.connect();
|
|
1213
|
-
// close() should not throw for UDP
|
|
1214
|
-
connection.close();
|
|
1215
|
-
});
|
|
1216
|
-
|
|
1217
|
-
test("DenoUdpSyslogConnection send timeout", async () => {
|
|
1218
|
-
// Use a non-routable IP to trigger timeout
|
|
1219
|
-
const connection = new DenoUdpSyslogConnection("10.255.255.1", 9999, 50); // Very short timeout
|
|
1220
|
-
connection.connect();
|
|
1221
|
-
|
|
1222
|
-
try {
|
|
1223
|
-
await connection.send("test message");
|
|
1224
|
-
// If we reach here, the send didn't timeout as expected
|
|
1225
|
-
// This might happen if the system is very fast or network conditions are unusual
|
|
1226
|
-
} catch (error) {
|
|
1227
|
-
// This is expected - either timeout or network unreachable
|
|
1228
|
-
assertEquals(typeof (error as Error).message, "string");
|
|
1229
|
-
} finally {
|
|
1230
|
-
connection.close();
|
|
1231
|
-
}
|
|
1232
|
-
});
|
|
1233
|
-
|
|
1234
|
-
test("DenoTcpSyslogConnection instantiation", () => {
|
|
1235
|
-
const connection = new DenoTcpSyslogConnection("localhost", 514, 5000);
|
|
1236
|
-
assertInstanceOf(connection, DenoTcpSyslogConnection);
|
|
1237
|
-
});
|
|
1238
|
-
|
|
1239
|
-
test("DenoTcpSyslogConnection close without connection", () => {
|
|
1240
|
-
const connection = new DenoTcpSyslogConnection("localhost", 514, 5000);
|
|
1241
|
-
// close() should not throw even without connection
|
|
1242
|
-
connection.close();
|
|
1243
|
-
});
|
|
1244
|
-
|
|
1245
|
-
test("DenoTcpSyslogConnection connection timeout", async () => {
|
|
1246
|
-
// Use a non-routable IP address to ensure connection failure
|
|
1247
|
-
const connection = new DenoTcpSyslogConnection("10.255.255.1", 9999, 100); // Very short timeout
|
|
1248
|
-
|
|
1249
|
-
try {
|
|
1250
|
-
await assertRejects(
|
|
1251
|
-
() => connection.connect(),
|
|
1252
|
-
Error,
|
|
1253
|
-
);
|
|
1254
|
-
} finally {
|
|
1255
|
-
// Ensure cleanup
|
|
1256
|
-
connection.close();
|
|
1257
|
-
}
|
|
1258
|
-
});
|
|
1259
|
-
|
|
1260
|
-
test("DenoTcpSyslogConnection send without connection", async () => {
|
|
1261
|
-
const connection = new DenoTcpSyslogConnection("localhost", 514, 5000);
|
|
1262
|
-
|
|
1263
|
-
await assertRejects(
|
|
1264
|
-
() => connection.send("test message"),
|
|
1265
|
-
Error,
|
|
1266
|
-
"Connection not established",
|
|
1267
|
-
);
|
|
1268
|
-
});
|
|
1269
|
-
|
|
1270
|
-
test("DenoUdpSyslogConnection actual send test", async () => {
|
|
1271
|
-
// Try to send to a local UDP echo server or just verify the call doesn't crash
|
|
1272
|
-
const connection = new DenoUdpSyslogConnection("127.0.0.1", 1514, 1000); // Non-privileged port
|
|
1273
|
-
connection.connect();
|
|
1274
|
-
|
|
1275
|
-
try {
|
|
1276
|
-
// This will likely fail (no server listening), but should handle gracefully
|
|
1277
|
-
await connection.send("test syslog message");
|
|
1278
|
-
// If it succeeds, that's also fine - might have a server running
|
|
1279
|
-
} catch (error) {
|
|
1280
|
-
// Expected - likely no server listening, but the send mechanism should work
|
|
1281
|
-
const errorMessage = (error as Error).message;
|
|
1282
|
-
// Should contain either timeout or connection/network error
|
|
1283
|
-
assertEquals(typeof errorMessage, "string");
|
|
1284
|
-
} finally {
|
|
1285
|
-
connection.close();
|
|
1286
|
-
}
|
|
1287
|
-
});
|
|
1288
|
-
|
|
1289
|
-
test("DenoTcpSyslogConnection actual send test with mock server", async () => {
|
|
1290
|
-
// Create a simple TCP server to receive the message
|
|
1291
|
-
let receivedData = "";
|
|
1292
|
-
|
|
1293
|
-
const server = Deno.listen({ port: 0 }); // Let system choose port
|
|
1294
|
-
const serverAddr = server.addr as Deno.NetAddr;
|
|
1295
|
-
|
|
1296
|
-
// Handle one connection in background
|
|
1297
|
-
const serverTask = (async () => {
|
|
1298
|
-
try {
|
|
1299
|
-
const conn = await server.accept();
|
|
1300
|
-
const buffer = new Uint8Array(1024);
|
|
1301
|
-
const bytesRead = await conn.read(buffer);
|
|
1302
|
-
if (bytesRead) {
|
|
1303
|
-
receivedData = new TextDecoder().decode(
|
|
1304
|
-
buffer.subarray(0, bytesRead),
|
|
1305
|
-
);
|
|
1306
|
-
}
|
|
1307
|
-
conn.close();
|
|
1308
|
-
} catch {
|
|
1309
|
-
// Server closed or connection error
|
|
1310
|
-
}
|
|
1311
|
-
})();
|
|
1312
|
-
|
|
1313
|
-
try {
|
|
1314
|
-
// Give server a moment to start
|
|
1315
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
1316
|
-
|
|
1317
|
-
// Connect and send message - create new connection with no timeout to avoid timer leaks
|
|
1318
|
-
const connection = new DenoTcpSyslogConnection(
|
|
1319
|
-
"127.0.0.1",
|
|
1320
|
-
serverAddr.port,
|
|
1321
|
-
0,
|
|
1322
|
-
); // No timeout
|
|
1323
|
-
|
|
1324
|
-
await connection.connect();
|
|
1325
|
-
await connection.send("test syslog message from Deno TCP");
|
|
1326
|
-
connection.close();
|
|
1327
|
-
|
|
1328
|
-
// Give server time to process
|
|
1329
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
1330
|
-
|
|
1331
|
-
// Verify message was received
|
|
1332
|
-
assertEquals(
|
|
1333
|
-
receivedData.includes("test syslog message from Deno TCP"),
|
|
1334
|
-
true,
|
|
1335
|
-
);
|
|
1336
|
-
} finally {
|
|
1337
|
-
server.close();
|
|
1338
|
-
await serverTask.catch(() => {}); // Wait for server cleanup
|
|
1339
|
-
}
|
|
1340
|
-
});
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
|
-
// Node.js/Bun-specific tests
|
|
1344
|
-
test("NodeUdpSyslogConnection instantiation", () => {
|
|
1345
|
-
const connection = new NodeUdpSyslogConnection("localhost", 514, 5000);
|
|
1346
|
-
assertInstanceOf(connection, NodeUdpSyslogConnection);
|
|
1347
|
-
});
|
|
1348
|
-
|
|
1349
|
-
test("NodeUdpSyslogConnection connect and close", () => {
|
|
1350
|
-
const connection = new NodeUdpSyslogConnection("localhost", 514, 5000);
|
|
1351
|
-
// connect() should not throw for UDP
|
|
1352
|
-
connection.connect();
|
|
1353
|
-
// close() should not throw for UDP
|
|
1354
|
-
connection.close();
|
|
1355
|
-
});
|
|
1356
|
-
|
|
1357
|
-
test("NodeUdpSyslogConnection send timeout", async () => {
|
|
1358
|
-
// Use a non-routable IP to trigger timeout
|
|
1359
|
-
const connection = new NodeUdpSyslogConnection("10.255.255.1", 9999, 50); // Very short timeout
|
|
1360
|
-
connection.connect();
|
|
1361
|
-
|
|
1362
|
-
try {
|
|
1363
|
-
await connection.send("test message");
|
|
1364
|
-
// If we reach here, the send didn't timeout as expected
|
|
1365
|
-
// This might happen if the system is very fast or network conditions are unusual
|
|
1366
|
-
} catch (error) {
|
|
1367
|
-
// This is expected - either timeout or network unreachable
|
|
1368
|
-
assertEquals(typeof (error as Error).message, "string");
|
|
1369
|
-
} finally {
|
|
1370
|
-
connection.close();
|
|
1371
|
-
}
|
|
1372
|
-
});
|
|
1373
|
-
|
|
1374
|
-
test("NodeTcpSyslogConnection instantiation", () => {
|
|
1375
|
-
const connection = new NodeTcpSyslogConnection("localhost", 514, 5000);
|
|
1376
|
-
assertInstanceOf(connection, NodeTcpSyslogConnection);
|
|
1377
|
-
});
|
|
1378
|
-
|
|
1379
|
-
test("NodeTcpSyslogConnection close without connection", () => {
|
|
1380
|
-
const connection = new NodeTcpSyslogConnection("localhost", 514, 5000);
|
|
1381
|
-
// close() should not throw even without connection
|
|
1382
|
-
connection.close();
|
|
1383
|
-
});
|
|
1384
|
-
|
|
1385
|
-
test("NodeTcpSyslogConnection connection timeout", {
|
|
1386
|
-
sanitizeResources: false,
|
|
1387
|
-
sanitizeOps: false,
|
|
1388
|
-
}, async () => {
|
|
1389
|
-
// Use a non-routable IP address to ensure connection failure
|
|
1390
|
-
const connection = new NodeTcpSyslogConnection("10.255.255.1", 9999, 100); // Very short timeout
|
|
1391
|
-
|
|
1392
|
-
try {
|
|
1393
|
-
await assertRejects(
|
|
1394
|
-
() => connection.connect(),
|
|
1395
|
-
Error,
|
|
1396
|
-
);
|
|
1397
|
-
} finally {
|
|
1398
|
-
// Ensure cleanup
|
|
1399
|
-
connection.close();
|
|
1400
|
-
}
|
|
1401
|
-
});
|
|
1402
|
-
|
|
1403
|
-
test("NodeTcpSyslogConnection send without connection", () => {
|
|
1404
|
-
const connection = new NodeTcpSyslogConnection("localhost", 514, 5000);
|
|
1405
|
-
|
|
1406
|
-
assertThrows(
|
|
1407
|
-
() => connection.send("test message"),
|
|
1408
|
-
Error,
|
|
1409
|
-
"Connection not established",
|
|
1410
|
-
);
|
|
1411
|
-
});
|
|
1412
|
-
|
|
1413
|
-
test("NodeUdpSyslogConnection actual send test", async () => {
|
|
1414
|
-
// Try to send to a local UDP port
|
|
1415
|
-
const connection = new NodeUdpSyslogConnection("127.0.0.1", 1514, 1000); // Non-privileged port
|
|
1416
|
-
connection.connect();
|
|
1417
|
-
|
|
1418
|
-
try {
|
|
1419
|
-
// This will likely fail (no server listening), but should handle gracefully
|
|
1420
|
-
await connection.send("test syslog message");
|
|
1421
|
-
// If it succeeds, that's also fine - might have a server running
|
|
1422
|
-
} catch (error) {
|
|
1423
|
-
// Expected - likely no server listening, but the send mechanism should work
|
|
1424
|
-
const errorMessage = (error as Error).message;
|
|
1425
|
-
// Should contain either timeout or connection/network error
|
|
1426
|
-
assertEquals(typeof errorMessage, "string");
|
|
1427
|
-
} finally {
|
|
1428
|
-
connection.close();
|
|
1429
|
-
}
|
|
1430
|
-
});
|
|
1431
|
-
|
|
1432
|
-
test("NodeTcpSyslogConnection actual send test with mock server", async () => {
|
|
1433
|
-
// Import Node.js modules for creating a server
|
|
1434
|
-
|
|
1435
|
-
let receivedData = "";
|
|
1436
|
-
|
|
1437
|
-
// Create a simple TCP server
|
|
1438
|
-
const server = createServer((socket) => {
|
|
1439
|
-
socket.on("data", (data) => {
|
|
1440
|
-
receivedData = data.toString();
|
|
1441
|
-
socket.end();
|
|
1442
|
-
});
|
|
1443
|
-
});
|
|
1444
|
-
|
|
1445
|
-
// Start server on random port
|
|
1446
|
-
await new Promise<void>((resolve) => {
|
|
1447
|
-
server.listen(0, "127.0.0.1", resolve);
|
|
1448
|
-
});
|
|
1449
|
-
|
|
1450
|
-
const address = server.address() as { port: number };
|
|
1451
|
-
|
|
1452
|
-
try {
|
|
1453
|
-
// Connect and send message
|
|
1454
|
-
const connection = new NodeTcpSyslogConnection(
|
|
1455
|
-
"127.0.0.1",
|
|
1456
|
-
address.port,
|
|
1457
|
-
5000,
|
|
1458
|
-
);
|
|
1459
|
-
|
|
1460
|
-
await connection.connect();
|
|
1461
|
-
await connection.send("test syslog message from Node TCP");
|
|
1462
|
-
connection.close();
|
|
1463
|
-
|
|
1464
|
-
// Wait a bit for server to receive data
|
|
1465
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1466
|
-
|
|
1467
|
-
// Verify message was received
|
|
1468
|
-
assertEquals(
|
|
1469
|
-
receivedData.includes("test syslog message from Node TCP"),
|
|
1470
|
-
true,
|
|
1471
|
-
);
|
|
1472
|
-
} finally {
|
|
1473
|
-
server.close();
|
|
1474
|
-
}
|
|
1475
|
-
});
|