observability-toolkit 1.1.0 → 1.4.0
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/README.md +52 -3
- package/dist/backends/index.d.ts +28 -0
- package/dist/backends/index.d.ts.map +1 -1
- package/dist/backends/local-jsonl.d.ts +29 -1
- package/dist/backends/local-jsonl.d.ts.map +1 -1
- package/dist/backends/local-jsonl.js +259 -27
- package/dist/backends/local-jsonl.js.map +1 -1
- package/dist/backends/local-jsonl.test.d.ts +2 -0
- package/dist/backends/local-jsonl.test.d.ts.map +1 -0
- package/dist/backends/local-jsonl.test.js +1638 -0
- package/dist/backends/local-jsonl.test.js.map +1 -0
- package/dist/backends/signoz-api.d.ts +9 -2
- package/dist/backends/signoz-api.d.ts.map +1 -1
- package/dist/backends/signoz-api.integration.test.d.ts +8 -0
- package/dist/backends/signoz-api.integration.test.d.ts.map +1 -0
- package/dist/backends/signoz-api.integration.test.js +137 -0
- package/dist/backends/signoz-api.integration.test.js.map +1 -0
- package/dist/backends/signoz-api.js +206 -115
- package/dist/backends/signoz-api.js.map +1 -1
- package/dist/backends/signoz-api.test.d.ts +2 -0
- package/dist/backends/signoz-api.test.d.ts.map +1 -0
- package/dist/backends/signoz-api.test.js +1080 -0
- package/dist/backends/signoz-api.test.js.map +1 -0
- package/dist/lib/constants.d.ts +28 -0
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +73 -0
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/constants.test.d.ts +5 -0
- package/dist/lib/constants.test.d.ts.map +1 -0
- package/dist/lib/constants.test.js +381 -0
- package/dist/lib/constants.test.js.map +1 -0
- package/dist/lib/file-utils.d.ts +53 -1
- package/dist/lib/file-utils.d.ts.map +1 -1
- package/dist/lib/file-utils.js +142 -3
- package/dist/lib/file-utils.js.map +1 -1
- package/dist/lib/file-utils.test.d.ts +2 -0
- package/dist/lib/file-utils.test.d.ts.map +1 -0
- package/dist/lib/file-utils.test.js +649 -0
- package/dist/lib/file-utils.test.js.map +1 -0
- package/dist/server.js +50 -63
- package/dist/server.js.map +1 -1
- package/dist/server.test.d.ts +5 -0
- package/dist/server.test.d.ts.map +1 -0
- package/dist/server.test.js +547 -0
- package/dist/server.test.js.map +1 -0
- package/dist/tools/context-stats.d.ts +2 -2
- package/dist/tools/context-stats.d.ts.map +1 -1
- package/dist/tools/context-stats.js +2 -1
- package/dist/tools/context-stats.js.map +1 -1
- package/dist/tools/context-stats.test.d.ts +5 -0
- package/dist/tools/context-stats.test.d.ts.map +1 -0
- package/dist/tools/context-stats.test.js +465 -0
- package/dist/tools/context-stats.test.js.map +1 -0
- package/dist/tools/get-trace-url.d.ts.map +1 -1
- package/dist/tools/get-trace-url.js +5 -1
- package/dist/tools/get-trace-url.js.map +1 -1
- package/dist/tools/get-trace-url.test.d.ts +5 -0
- package/dist/tools/get-trace-url.test.d.ts.map +1 -0
- package/dist/tools/get-trace-url.test.js +429 -0
- package/dist/tools/get-trace-url.test.js.map +1 -0
- package/dist/tools/health-check.d.ts +9 -2
- package/dist/tools/health-check.d.ts.map +1 -1
- package/dist/tools/health-check.js +66 -27
- package/dist/tools/health-check.js.map +1 -1
- package/dist/tools/health-check.test.d.ts +5 -0
- package/dist/tools/health-check.test.d.ts.map +1 -0
- package/dist/tools/health-check.test.js +386 -0
- package/dist/tools/health-check.test.js.map +1 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/query-llm-events.d.ts +82 -0
- package/dist/tools/query-llm-events.d.ts.map +1 -0
- package/dist/tools/query-llm-events.js +60 -0
- package/dist/tools/query-llm-events.js.map +1 -0
- package/dist/tools/query-llm-events.test.d.ts +5 -0
- package/dist/tools/query-llm-events.test.d.ts.map +1 -0
- package/dist/tools/query-llm-events.test.js +111 -0
- package/dist/tools/query-llm-events.test.js.map +1 -0
- package/dist/tools/query-logs.d.ts +15 -8
- package/dist/tools/query-logs.d.ts.map +1 -1
- package/dist/tools/query-logs.js +11 -10
- package/dist/tools/query-logs.js.map +1 -1
- package/dist/tools/query-logs.test.d.ts +5 -0
- package/dist/tools/query-logs.test.d.ts.map +1 -0
- package/dist/tools/query-logs.test.js +688 -0
- package/dist/tools/query-logs.test.js.map +1 -0
- package/dist/tools/query-metrics.d.ts +13 -15
- package/dist/tools/query-metrics.d.ts.map +1 -1
- package/dist/tools/query-metrics.js +12 -13
- package/dist/tools/query-metrics.js.map +1 -1
- package/dist/tools/query-metrics.test.d.ts +5 -0
- package/dist/tools/query-metrics.test.d.ts.map +1 -0
- package/dist/tools/query-metrics.test.js +597 -0
- package/dist/tools/query-metrics.test.js.map +1 -0
- package/dist/tools/query-traces.d.ts +19 -14
- package/dist/tools/query-traces.d.ts.map +1 -1
- package/dist/tools/query-traces.js +14 -14
- package/dist/tools/query-traces.js.map +1 -1
- package/dist/tools/query-traces.test.d.ts +5 -0
- package/dist/tools/query-traces.test.d.ts.map +1 -0
- package/dist/tools/query-traces.test.js +643 -0
- package/dist/tools/query-traces.test.js.map +1 -0
- package/dist/tools/setup-claudeignore.d.ts +36 -10
- package/dist/tools/setup-claudeignore.d.ts.map +1 -1
- package/dist/tools/setup-claudeignore.js +193 -33
- package/dist/tools/setup-claudeignore.js.map +1 -1
- package/dist/tools/setup-claudeignore.test.d.ts +2 -0
- package/dist/tools/setup-claudeignore.test.d.ts.map +1 -0
- package/dist/tools/setup-claudeignore.test.js +481 -0
- package/dist/tools/setup-claudeignore.test.js.map +1 -0
- package/dist/tools/signoz.integration.test.d.ts +8 -0
- package/dist/tools/signoz.integration.test.d.ts.map +1 -0
- package/dist/tools/signoz.integration.test.js +141 -0
- package/dist/tools/signoz.integration.test.js.map +1 -0
- package/package.json +6 -3
|
@@ -0,0 +1,649 @@
|
|
|
1
|
+
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import * as os from 'os';
|
|
6
|
+
import { expandTilde, loadJson, loadJsonSafe, getDateString, listFiles, parseDateFromFilename, readJsonlSync, readJsonlSyncWithStats, streamJsonl, filterByDateRange, paginateResults, hasReachedLimit, cleanupOldFiles, } from './file-utils.js';
|
|
7
|
+
describe('file-utils', () => {
|
|
8
|
+
let testDir;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
// Create a unique temp directory for each test
|
|
11
|
+
testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'file-utils-test-'));
|
|
12
|
+
});
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
// Clean up temp directory
|
|
15
|
+
if (testDir && fs.existsSync(testDir)) {
|
|
16
|
+
fs.rmSync(testDir, { recursive: true, force: true });
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
describe('expandTilde', () => {
|
|
20
|
+
it('should return path as-is if it does not start with tilde', () => {
|
|
21
|
+
const result = expandTilde('/absolute/path');
|
|
22
|
+
assert.strictEqual(result, '/absolute/path');
|
|
23
|
+
});
|
|
24
|
+
it('should expand tilde to home directory for ~ alone', () => {
|
|
25
|
+
const result = expandTilde('~');
|
|
26
|
+
assert.strictEqual(result, os.homedir());
|
|
27
|
+
});
|
|
28
|
+
it('should expand tilde to home directory for ~/path', () => {
|
|
29
|
+
const result = expandTilde('~/documents/file.txt');
|
|
30
|
+
assert.strictEqual(result, path.join(os.homedir(), 'documents/file.txt'));
|
|
31
|
+
});
|
|
32
|
+
it('should return ~user/path as-is (not supported)', () => {
|
|
33
|
+
const result = expandTilde('~someuser/path');
|
|
34
|
+
assert.strictEqual(result, '~someuser/path');
|
|
35
|
+
});
|
|
36
|
+
it('should handle multiple directory levels', () => {
|
|
37
|
+
const result = expandTilde('~/a/b/c/d');
|
|
38
|
+
assert.strictEqual(result, path.join(os.homedir(), 'a/b/c/d'));
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe('loadJson', () => {
|
|
42
|
+
it('should load and parse valid JSON file', () => {
|
|
43
|
+
const testData = { key: 'value', num: 42 };
|
|
44
|
+
const filePath = path.join(testDir, 'test.json');
|
|
45
|
+
fs.writeFileSync(filePath, JSON.stringify(testData));
|
|
46
|
+
const result = loadJson(filePath);
|
|
47
|
+
assert.deepStrictEqual(result, testData);
|
|
48
|
+
});
|
|
49
|
+
it('should return null if file does not exist', () => {
|
|
50
|
+
const result = loadJson(path.join(testDir, 'nonexistent.json'));
|
|
51
|
+
assert.strictEqual(result, null);
|
|
52
|
+
});
|
|
53
|
+
it('should return null on JSON parse error', () => {
|
|
54
|
+
const filePath = path.join(testDir, 'invalid.json');
|
|
55
|
+
fs.writeFileSync(filePath, 'invalid json {');
|
|
56
|
+
const result = loadJson(filePath);
|
|
57
|
+
assert.strictEqual(result, null);
|
|
58
|
+
});
|
|
59
|
+
it('should return fallback value if file does not exist', () => {
|
|
60
|
+
const fallback = { default: true };
|
|
61
|
+
const result = loadJson(path.join(testDir, 'nonexistent.json'), fallback);
|
|
62
|
+
assert.deepStrictEqual(result, fallback);
|
|
63
|
+
});
|
|
64
|
+
it('should return fallback value on parse error', () => {
|
|
65
|
+
const fallback = { default: true };
|
|
66
|
+
const filePath = path.join(testDir, 'invalid.json');
|
|
67
|
+
fs.writeFileSync(filePath, 'invalid json');
|
|
68
|
+
const result = loadJson(filePath, fallback);
|
|
69
|
+
assert.deepStrictEqual(result, fallback);
|
|
70
|
+
});
|
|
71
|
+
it('should handle empty JSON file', () => {
|
|
72
|
+
const filePath = path.join(testDir, 'empty.json');
|
|
73
|
+
fs.writeFileSync(filePath, '');
|
|
74
|
+
const result = loadJson(filePath);
|
|
75
|
+
assert.strictEqual(result, null);
|
|
76
|
+
});
|
|
77
|
+
it('should parse complex nested JSON', () => {
|
|
78
|
+
const testData = {
|
|
79
|
+
nested: { deep: { value: 'test' } },
|
|
80
|
+
array: [1, 2, 3],
|
|
81
|
+
};
|
|
82
|
+
const filePath = path.join(testDir, 'complex.json');
|
|
83
|
+
fs.writeFileSync(filePath, JSON.stringify(testData));
|
|
84
|
+
const result = loadJson(filePath);
|
|
85
|
+
assert.deepStrictEqual(result, testData);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe('loadJsonSafe', () => {
|
|
89
|
+
it('should return loaded JSON if file exists and is valid', () => {
|
|
90
|
+
const testData = { key: 'value' };
|
|
91
|
+
const fallback = { fallback: true };
|
|
92
|
+
const filePath = path.join(testDir, 'test.json');
|
|
93
|
+
fs.writeFileSync(filePath, JSON.stringify(testData));
|
|
94
|
+
const result = loadJsonSafe(filePath, fallback);
|
|
95
|
+
assert.deepStrictEqual(result, testData);
|
|
96
|
+
});
|
|
97
|
+
it('should return fallback if file does not exist', () => {
|
|
98
|
+
const fallback = { fallback: true };
|
|
99
|
+
const result = loadJsonSafe(path.join(testDir, 'nonexistent.json'), fallback);
|
|
100
|
+
assert.deepStrictEqual(result, fallback);
|
|
101
|
+
});
|
|
102
|
+
it('should return fallback on parse error', () => {
|
|
103
|
+
const fallback = { fallback: true };
|
|
104
|
+
const filePath = path.join(testDir, 'invalid.json');
|
|
105
|
+
fs.writeFileSync(filePath, 'invalid json');
|
|
106
|
+
const result = loadJsonSafe(filePath, fallback);
|
|
107
|
+
assert.deepStrictEqual(result, fallback);
|
|
108
|
+
});
|
|
109
|
+
it('should never return null', () => {
|
|
110
|
+
const fallback = { key: 'default' };
|
|
111
|
+
const result = loadJsonSafe(path.join(testDir, 'nonexistent.json'), fallback);
|
|
112
|
+
assert.notStrictEqual(result, null);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
describe('getDateString', () => {
|
|
116
|
+
it('should format date as YYYY-MM-DD', () => {
|
|
117
|
+
const date = new Date('2026-01-28T12:34:56.789Z');
|
|
118
|
+
const result = getDateString(date);
|
|
119
|
+
assert.strictEqual(result, '2026-01-28');
|
|
120
|
+
});
|
|
121
|
+
it('should use current date if none provided', () => {
|
|
122
|
+
const result = getDateString();
|
|
123
|
+
const now = new Date();
|
|
124
|
+
const expected = now.toISOString().split('T')[0];
|
|
125
|
+
assert.strictEqual(result, expected);
|
|
126
|
+
});
|
|
127
|
+
it('should handle leap year date', () => {
|
|
128
|
+
const date = new Date('2024-02-29T00:00:00.000Z');
|
|
129
|
+
const result = getDateString(date);
|
|
130
|
+
assert.strictEqual(result, '2024-02-29');
|
|
131
|
+
});
|
|
132
|
+
it('should handle year boundary', () => {
|
|
133
|
+
const date = new Date('2025-12-31T23:59:59.999Z');
|
|
134
|
+
const result = getDateString(date);
|
|
135
|
+
assert.strictEqual(result, '2025-12-31');
|
|
136
|
+
});
|
|
137
|
+
it('should handle January 1st', () => {
|
|
138
|
+
const date = new Date('2026-01-01T00:00:00.000Z');
|
|
139
|
+
const result = getDateString(date);
|
|
140
|
+
assert.strictEqual(result, '2026-01-01');
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
describe('listFiles', () => {
|
|
144
|
+
it('should return empty array if directory does not exist', () => {
|
|
145
|
+
const result = listFiles(path.join(testDir, 'nonexistent'), /\.json$/);
|
|
146
|
+
assert.deepStrictEqual(result, []);
|
|
147
|
+
});
|
|
148
|
+
it('should filter files by pattern', () => {
|
|
149
|
+
fs.writeFileSync(path.join(testDir, 'file1.json'), '{}');
|
|
150
|
+
fs.writeFileSync(path.join(testDir, 'file2.json'), '{}');
|
|
151
|
+
fs.writeFileSync(path.join(testDir, 'file3.txt'), 'text');
|
|
152
|
+
fs.writeFileSync(path.join(testDir, 'file4.json'), '{}');
|
|
153
|
+
const result = listFiles(testDir, /\.json$/);
|
|
154
|
+
assert.strictEqual(result.length, 3);
|
|
155
|
+
assert(result.every(f => f.endsWith('.json')));
|
|
156
|
+
});
|
|
157
|
+
it('should return full paths', () => {
|
|
158
|
+
fs.writeFileSync(path.join(testDir, 'file1.json'), '{}');
|
|
159
|
+
fs.writeFileSync(path.join(testDir, 'file2.json'), '{}');
|
|
160
|
+
const result = listFiles(testDir, /\.json$/);
|
|
161
|
+
assert(result.every(f => f.startsWith(testDir)));
|
|
162
|
+
});
|
|
163
|
+
it('should sort by modification time (newest first)', async () => {
|
|
164
|
+
// Create files with different modification times
|
|
165
|
+
const file1 = path.join(testDir, 'file1.json');
|
|
166
|
+
const file2 = path.join(testDir, 'file2.json');
|
|
167
|
+
const file3 = path.join(testDir, 'file3.json');
|
|
168
|
+
fs.writeFileSync(file1, '{}');
|
|
169
|
+
// Small delay to ensure different mtimes
|
|
170
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
171
|
+
fs.writeFileSync(file2, '{}');
|
|
172
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
173
|
+
fs.writeFileSync(file3, '{}');
|
|
174
|
+
const result = listFiles(testDir, /\.json$/);
|
|
175
|
+
assert.strictEqual(result[0], file3); // newest first
|
|
176
|
+
assert.strictEqual(result[1], file2);
|
|
177
|
+
assert.strictEqual(result[2], file1); // oldest last
|
|
178
|
+
});
|
|
179
|
+
it('should handle empty directory', () => {
|
|
180
|
+
const result = listFiles(testDir, /\.json$/);
|
|
181
|
+
assert.deepStrictEqual(result, []);
|
|
182
|
+
});
|
|
183
|
+
it('should match complex regex patterns', () => {
|
|
184
|
+
fs.writeFileSync(path.join(testDir, 'traces-2026-01-28.jsonl'), '{}');
|
|
185
|
+
fs.writeFileSync(path.join(testDir, 'metrics-2026-01-28.jsonl'), '{}');
|
|
186
|
+
fs.writeFileSync(path.join(testDir, 'logs-2026-01-28.jsonl'), '{}');
|
|
187
|
+
fs.writeFileSync(path.join(testDir, 'other.txt'), 'text');
|
|
188
|
+
const result = listFiles(testDir, /^\w+-\d{4}-\d{2}-\d{2}\.jsonl$/);
|
|
189
|
+
assert.strictEqual(result.length, 3);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
describe('parseDateFromFilename', () => {
|
|
193
|
+
it('should extract date from filename with format traces-YYYY-MM-DD.jsonl', () => {
|
|
194
|
+
const result = parseDateFromFilename('traces-2026-01-28.jsonl');
|
|
195
|
+
assert.strictEqual(result, '2026-01-28');
|
|
196
|
+
});
|
|
197
|
+
it('should extract date from filename with format metrics-YYYY-MM-DD.jsonl', () => {
|
|
198
|
+
const result = parseDateFromFilename('metrics-2026-12-25.jsonl');
|
|
199
|
+
assert.strictEqual(result, '2026-12-25');
|
|
200
|
+
});
|
|
201
|
+
it('should return null if no date found', () => {
|
|
202
|
+
const result = parseDateFromFilename('nodatehere.jsonl');
|
|
203
|
+
assert.strictEqual(result, null);
|
|
204
|
+
});
|
|
205
|
+
it('should handle full path', () => {
|
|
206
|
+
const result = parseDateFromFilename('/path/to/traces-2026-01-28.jsonl');
|
|
207
|
+
assert.strictEqual(result, '2026-01-28');
|
|
208
|
+
});
|
|
209
|
+
it('should extract first matching date pattern', () => {
|
|
210
|
+
const result = parseDateFromFilename('traces-2026-01-28-backup-2026-01-29.jsonl');
|
|
211
|
+
assert.strictEqual(result, '2026-01-28');
|
|
212
|
+
});
|
|
213
|
+
it('should handle invalid date numbers', () => {
|
|
214
|
+
const result = parseDateFromFilename('file-9999-99-99.jsonl');
|
|
215
|
+
assert.strictEqual(result, '9999-99-99');
|
|
216
|
+
});
|
|
217
|
+
it('should return null for incomplete date patterns', () => {
|
|
218
|
+
const result = parseDateFromFilename('file-2026-01.jsonl');
|
|
219
|
+
assert.strictEqual(result, null);
|
|
220
|
+
});
|
|
221
|
+
it('should handle leap year date', () => {
|
|
222
|
+
const result = parseDateFromFilename('traces-2024-02-29.jsonl');
|
|
223
|
+
assert.strictEqual(result, '2024-02-29');
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
describe('readJsonlSync', () => {
|
|
227
|
+
it('should return empty array if file does not exist', () => {
|
|
228
|
+
const result = readJsonlSync(path.join(testDir, 'nonexistent.jsonl'));
|
|
229
|
+
assert.deepStrictEqual(result, []);
|
|
230
|
+
});
|
|
231
|
+
it('should parse JSONL file with multiple lines', () => {
|
|
232
|
+
const filePath = path.join(testDir, 'test.jsonl');
|
|
233
|
+
fs.writeFileSync(filePath, '{"id":1,"name":"first"}\n{"id":2,"name":"second"}\n{"id":3,"name":"third"}');
|
|
234
|
+
const result = readJsonlSync(filePath);
|
|
235
|
+
assert.strictEqual(result.length, 3);
|
|
236
|
+
assert.deepStrictEqual(result[0], { id: 1, name: 'first' });
|
|
237
|
+
assert.deepStrictEqual(result[1], { id: 2, name: 'second' });
|
|
238
|
+
assert.deepStrictEqual(result[2], { id: 3, name: 'third' });
|
|
239
|
+
});
|
|
240
|
+
it('should skip empty lines', () => {
|
|
241
|
+
const filePath = path.join(testDir, 'test.jsonl');
|
|
242
|
+
fs.writeFileSync(filePath, '{"id":1}\n\n{"id":2}\n \n{"id":3}');
|
|
243
|
+
const result = readJsonlSync(filePath);
|
|
244
|
+
assert.strictEqual(result.length, 3);
|
|
245
|
+
});
|
|
246
|
+
it('should skip malformed JSON lines', () => {
|
|
247
|
+
const filePath = path.join(testDir, 'test.jsonl');
|
|
248
|
+
fs.writeFileSync(filePath, '{"id":1}\n{invalid json}\n{"id":2}');
|
|
249
|
+
const result = readJsonlSync(filePath);
|
|
250
|
+
assert.strictEqual(result.length, 2);
|
|
251
|
+
assert.deepStrictEqual(result[0], { id: 1 });
|
|
252
|
+
assert.deepStrictEqual(result[1], { id: 2 });
|
|
253
|
+
});
|
|
254
|
+
it('should respect limit parameter', () => {
|
|
255
|
+
const filePath = path.join(testDir, 'test.jsonl');
|
|
256
|
+
fs.writeFileSync(filePath, '{"id":1}\n{"id":2}\n{"id":3}\n{"id":4}\n{"id":5}');
|
|
257
|
+
const result = readJsonlSync(filePath, 3);
|
|
258
|
+
assert.strictEqual(result.length, 3);
|
|
259
|
+
});
|
|
260
|
+
it('should stop reading at limit even with more lines', () => {
|
|
261
|
+
const filePath = path.join(testDir, 'test.jsonl');
|
|
262
|
+
fs.writeFileSync(filePath, '{"id":1}\n{"id":2}\n{"id":3}\n{"id":4}');
|
|
263
|
+
const result = readJsonlSync(filePath, 2);
|
|
264
|
+
assert.strictEqual(result.length, 2);
|
|
265
|
+
assert.deepStrictEqual(result[1], { id: 2 });
|
|
266
|
+
});
|
|
267
|
+
it('should handle file with trailing whitespace', () => {
|
|
268
|
+
const filePath = path.join(testDir, 'test.jsonl');
|
|
269
|
+
fs.writeFileSync(filePath, '{"id":1}\n{"id":2}\n ');
|
|
270
|
+
const result = readJsonlSync(filePath);
|
|
271
|
+
assert.strictEqual(result.length, 2);
|
|
272
|
+
});
|
|
273
|
+
it('should handle single line file', () => {
|
|
274
|
+
const filePath = path.join(testDir, 'test.jsonl');
|
|
275
|
+
fs.writeFileSync(filePath, '{"id":1}');
|
|
276
|
+
const result = readJsonlSync(filePath);
|
|
277
|
+
assert.strictEqual(result.length, 1);
|
|
278
|
+
assert.deepStrictEqual(result[0], { id: 1 });
|
|
279
|
+
});
|
|
280
|
+
it('should handle complex JSON objects', () => {
|
|
281
|
+
const filePath = path.join(testDir, 'test.jsonl');
|
|
282
|
+
fs.writeFileSync(filePath, '{"nested":{"deep":{"value":1}}}\n{"array":[1,2,3]}');
|
|
283
|
+
const result = readJsonlSync(filePath);
|
|
284
|
+
assert.deepStrictEqual(result[0], { nested: { deep: { value: 1 } } });
|
|
285
|
+
assert.deepStrictEqual(result[1], { array: [1, 2, 3] });
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
describe('streamJsonl', () => {
|
|
289
|
+
it('should yield nothing if file does not exist', async () => {
|
|
290
|
+
const results = [];
|
|
291
|
+
for await (const item of streamJsonl(path.join(testDir, 'nonexistent.jsonl'))) {
|
|
292
|
+
results.push(item);
|
|
293
|
+
}
|
|
294
|
+
assert.strictEqual(results.length, 0);
|
|
295
|
+
});
|
|
296
|
+
it('should yield parsed JSON objects from file', async () => {
|
|
297
|
+
const filePath = path.join(testDir, 'test.jsonl');
|
|
298
|
+
fs.writeFileSync(filePath, '{"id":1}\n{"id":2}\n{"id":3}');
|
|
299
|
+
const results = [];
|
|
300
|
+
for await (const item of streamJsonl(filePath)) {
|
|
301
|
+
results.push(item);
|
|
302
|
+
}
|
|
303
|
+
assert.strictEqual(results.length, 3);
|
|
304
|
+
assert.deepStrictEqual(results[0], { id: 1 });
|
|
305
|
+
assert.deepStrictEqual(results[1], { id: 2 });
|
|
306
|
+
assert.deepStrictEqual(results[2], { id: 3 });
|
|
307
|
+
});
|
|
308
|
+
it('should skip empty lines', async () => {
|
|
309
|
+
const filePath = path.join(testDir, 'test.jsonl');
|
|
310
|
+
fs.writeFileSync(filePath, '{"id":1}\n\n \n{"id":2}');
|
|
311
|
+
const results = [];
|
|
312
|
+
for await (const item of streamJsonl(filePath)) {
|
|
313
|
+
results.push(item);
|
|
314
|
+
}
|
|
315
|
+
assert.strictEqual(results.length, 2);
|
|
316
|
+
});
|
|
317
|
+
it('should skip malformed JSON lines', async () => {
|
|
318
|
+
const filePath = path.join(testDir, 'test.jsonl');
|
|
319
|
+
fs.writeFileSync(filePath, '{"id":1}\n{invalid}\n{"id":2}');
|
|
320
|
+
const results = [];
|
|
321
|
+
for await (const item of streamJsonl(filePath)) {
|
|
322
|
+
results.push(item);
|
|
323
|
+
}
|
|
324
|
+
assert.strictEqual(results.length, 2);
|
|
325
|
+
assert.deepStrictEqual(results[0], { id: 1 });
|
|
326
|
+
assert.deepStrictEqual(results[1], { id: 2 });
|
|
327
|
+
});
|
|
328
|
+
it('should handle large JSONL files efficiently', async () => {
|
|
329
|
+
const filePath = path.join(testDir, 'large.jsonl');
|
|
330
|
+
const lines = Array.from({ length: 1000 }, (_, i) => JSON.stringify({ id: i })).join('\n');
|
|
331
|
+
fs.writeFileSync(filePath, lines);
|
|
332
|
+
let count = 0;
|
|
333
|
+
for await (const _item of streamJsonl(filePath)) {
|
|
334
|
+
count++;
|
|
335
|
+
}
|
|
336
|
+
assert.strictEqual(count, 1000);
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
describe('filterByDateRange', () => {
|
|
340
|
+
const items = [
|
|
341
|
+
{ id: 1, timestamp: '2026-01-10' },
|
|
342
|
+
{ id: 2, timestamp: '2026-01-15' },
|
|
343
|
+
{ id: 3, timestamp: '2026-01-20' },
|
|
344
|
+
{ id: 4, timestamp: '2026-01-25' },
|
|
345
|
+
];
|
|
346
|
+
it('should return all items if no date range specified', () => {
|
|
347
|
+
const result = filterByDateRange(items);
|
|
348
|
+
assert.strictEqual(result.length, 4);
|
|
349
|
+
});
|
|
350
|
+
it('should filter items after startDate', () => {
|
|
351
|
+
const result = filterByDateRange(items, '2026-01-15');
|
|
352
|
+
assert.strictEqual(result.length, 3);
|
|
353
|
+
assert.deepStrictEqual(result[0], { id: 2, timestamp: '2026-01-15' });
|
|
354
|
+
});
|
|
355
|
+
it('should filter items before endDate', () => {
|
|
356
|
+
const result = filterByDateRange(items, undefined, '2026-01-20');
|
|
357
|
+
assert.strictEqual(result.length, 3);
|
|
358
|
+
assert.deepStrictEqual(result[2], { id: 3, timestamp: '2026-01-20' });
|
|
359
|
+
});
|
|
360
|
+
it('should filter items between startDate and endDate', () => {
|
|
361
|
+
const result = filterByDateRange(items, '2026-01-12', '2026-01-22');
|
|
362
|
+
assert.strictEqual(result.length, 2);
|
|
363
|
+
assert.deepStrictEqual(result[0], { id: 2, timestamp: '2026-01-15' });
|
|
364
|
+
assert.deepStrictEqual(result[1], { id: 3, timestamp: '2026-01-20' });
|
|
365
|
+
});
|
|
366
|
+
it('should include items with exact startDate', () => {
|
|
367
|
+
const result = filterByDateRange(items, '2026-01-15');
|
|
368
|
+
assert(result.some(item => item.id === 2));
|
|
369
|
+
});
|
|
370
|
+
it('should include items with exact endDate', () => {
|
|
371
|
+
const result = filterByDateRange(items, undefined, '2026-01-15');
|
|
372
|
+
assert(result.some(item => item.id === 2));
|
|
373
|
+
});
|
|
374
|
+
it('should include items without timestamp', () => {
|
|
375
|
+
const itemsWithoutTs = [
|
|
376
|
+
{ id: 1, timestamp: '2026-01-10' },
|
|
377
|
+
{ id: 2 },
|
|
378
|
+
{ id: 3, timestamp: '2026-01-20' },
|
|
379
|
+
];
|
|
380
|
+
const result = filterByDateRange(itemsWithoutTs, '2026-01-15');
|
|
381
|
+
assert.strictEqual(result.length, 2);
|
|
382
|
+
assert(result.some(item => item.id === 2));
|
|
383
|
+
});
|
|
384
|
+
it('should return empty array if no items in range', () => {
|
|
385
|
+
const result = filterByDateRange(items, '2026-02-01', '2026-02-28');
|
|
386
|
+
assert.deepStrictEqual(result, []);
|
|
387
|
+
});
|
|
388
|
+
it('should handle empty items array', () => {
|
|
389
|
+
const result = filterByDateRange([], '2026-01-01', '2026-12-31');
|
|
390
|
+
assert.deepStrictEqual(result, []);
|
|
391
|
+
});
|
|
392
|
+
it('should work with time component in timestamp', () => {
|
|
393
|
+
const itemsWithTime = [
|
|
394
|
+
{ id: 1, timestamp: '2026-01-15T10:00:00' },
|
|
395
|
+
{ id: 2, timestamp: '2026-01-15T15:00:00' },
|
|
396
|
+
{ id: 3, timestamp: '2026-01-16T10:00:00' },
|
|
397
|
+
];
|
|
398
|
+
// Note: filterByDateRange uses lexicographic comparison
|
|
399
|
+
// '2026-01-15T...' > '2026-01-15' due to 'T', so use end of day
|
|
400
|
+
const result = filterByDateRange(itemsWithTime, '2026-01-15', '2026-01-15T23:59:59');
|
|
401
|
+
assert.strictEqual(result.length, 2);
|
|
402
|
+
});
|
|
403
|
+
it('should handle lexicographic string comparison correctly', () => {
|
|
404
|
+
const itemsWithDates = [
|
|
405
|
+
{ id: 1, timestamp: '2026-01-01' },
|
|
406
|
+
{ id: 2, timestamp: '2026-02-01' },
|
|
407
|
+
{ id: 3, timestamp: '2026-10-01' },
|
|
408
|
+
];
|
|
409
|
+
const result = filterByDateRange(itemsWithDates, '2026-02-01', '2026-10-01');
|
|
410
|
+
assert.strictEqual(result.length, 2);
|
|
411
|
+
});
|
|
412
|
+
it('should handle generic type with optional timestamp', () => {
|
|
413
|
+
const customItems = [
|
|
414
|
+
{ name: 'item1', timestamp: '2026-01-10' },
|
|
415
|
+
{ name: 'item2' },
|
|
416
|
+
];
|
|
417
|
+
const result = filterByDateRange(customItems, '2026-01-09', '2026-01-11');
|
|
418
|
+
assert.strictEqual(result.length, 2);
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
describe('paginateResults', () => {
|
|
422
|
+
const items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
423
|
+
it('should return items from offset to offset + limit', () => {
|
|
424
|
+
const result = paginateResults(items, 2, 3);
|
|
425
|
+
assert.deepStrictEqual(result, [3, 4, 5]);
|
|
426
|
+
});
|
|
427
|
+
it('should return from start when offset is 0', () => {
|
|
428
|
+
const result = paginateResults(items, 0, 5);
|
|
429
|
+
assert.deepStrictEqual(result, [1, 2, 3, 4, 5]);
|
|
430
|
+
});
|
|
431
|
+
it('should handle offset at end of array', () => {
|
|
432
|
+
const result = paginateResults(items, 8, 5);
|
|
433
|
+
assert.deepStrictEqual(result, [9, 10]);
|
|
434
|
+
});
|
|
435
|
+
it('should return empty array when offset exceeds length', () => {
|
|
436
|
+
const result = paginateResults(items, 15, 5);
|
|
437
|
+
assert.deepStrictEqual(result, []);
|
|
438
|
+
});
|
|
439
|
+
it('should handle limit larger than remaining items', () => {
|
|
440
|
+
const result = paginateResults(items, 7, 10);
|
|
441
|
+
assert.deepStrictEqual(result, [8, 9, 10]);
|
|
442
|
+
});
|
|
443
|
+
it('should handle empty array', () => {
|
|
444
|
+
const result = paginateResults([], 0, 10);
|
|
445
|
+
assert.deepStrictEqual(result, []);
|
|
446
|
+
});
|
|
447
|
+
it('should work with object arrays', () => {
|
|
448
|
+
const objs = [{ id: 1 }, { id: 2 }, { id: 3 }];
|
|
449
|
+
const result = paginateResults(objs, 1, 2);
|
|
450
|
+
assert.deepStrictEqual(result, [{ id: 2 }, { id: 3 }]);
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
describe('hasReachedLimit', () => {
|
|
454
|
+
it('should return true when results equal offset + limit', () => {
|
|
455
|
+
const result = hasReachedLimit(10, 5, 5);
|
|
456
|
+
assert.strictEqual(result, true);
|
|
457
|
+
});
|
|
458
|
+
it('should return true when results exceed offset + limit', () => {
|
|
459
|
+
const result = hasReachedLimit(15, 5, 5);
|
|
460
|
+
assert.strictEqual(result, true);
|
|
461
|
+
});
|
|
462
|
+
it('should return false when results are less than offset + limit', () => {
|
|
463
|
+
const result = hasReachedLimit(8, 5, 5);
|
|
464
|
+
assert.strictEqual(result, false);
|
|
465
|
+
});
|
|
466
|
+
it('should return false with zero results', () => {
|
|
467
|
+
const result = hasReachedLimit(0, 0, 10);
|
|
468
|
+
assert.strictEqual(result, false);
|
|
469
|
+
});
|
|
470
|
+
it('should handle zero offset', () => {
|
|
471
|
+
const result = hasReachedLimit(10, 0, 10);
|
|
472
|
+
assert.strictEqual(result, true);
|
|
473
|
+
});
|
|
474
|
+
});
|
|
475
|
+
describe('readJsonlSyncWithStats', () => {
|
|
476
|
+
it('should return data and stats', () => {
|
|
477
|
+
const filePath = path.join(testDir, 'test.jsonl');
|
|
478
|
+
fs.writeFileSync(filePath, '{"id":1}\n{"id":2}\n{"id":3}');
|
|
479
|
+
const result = readJsonlSyncWithStats(filePath);
|
|
480
|
+
assert.ok('data' in result);
|
|
481
|
+
assert.ok('stats' in result);
|
|
482
|
+
assert.strictEqual(result.data.length, 3);
|
|
483
|
+
assert.strictEqual(result.stats.totalLines, 3);
|
|
484
|
+
assert.strictEqual(result.stats.parsedLines, 3);
|
|
485
|
+
assert.strictEqual(result.stats.skippedLines, 0);
|
|
486
|
+
assert.strictEqual(result.stats.emptyLines, 0);
|
|
487
|
+
});
|
|
488
|
+
it('should count skipped lines', () => {
|
|
489
|
+
const filePath = path.join(testDir, 'test.jsonl');
|
|
490
|
+
fs.writeFileSync(filePath, '{"id":1}\n{invalid json}\n{"id":2}\n{also invalid}');
|
|
491
|
+
const result = readJsonlSyncWithStats(filePath);
|
|
492
|
+
assert.strictEqual(result.data.length, 2);
|
|
493
|
+
assert.strictEqual(result.stats.parsedLines, 2);
|
|
494
|
+
assert.strictEqual(result.stats.skippedLines, 2);
|
|
495
|
+
});
|
|
496
|
+
it('should count empty lines', () => {
|
|
497
|
+
const filePath = path.join(testDir, 'test.jsonl');
|
|
498
|
+
// Note: trailing empty lines are removed by trim(), so only internal empty lines are counted
|
|
499
|
+
fs.writeFileSync(filePath, '{"id":1}\n\n \n{"id":2}');
|
|
500
|
+
const result = readJsonlSyncWithStats(filePath);
|
|
501
|
+
assert.strictEqual(result.data.length, 2);
|
|
502
|
+
assert.strictEqual(result.stats.emptyLines, 2);
|
|
503
|
+
});
|
|
504
|
+
it('should respect limit parameter', () => {
|
|
505
|
+
const filePath = path.join(testDir, 'test.jsonl');
|
|
506
|
+
fs.writeFileSync(filePath, '{"id":1}\n{"id":2}\n{"id":3}\n{"id":4}\n{"id":5}');
|
|
507
|
+
const result = readJsonlSyncWithStats(filePath, 3);
|
|
508
|
+
assert.strictEqual(result.data.length, 3);
|
|
509
|
+
assert.strictEqual(result.stats.parsedLines, 3);
|
|
510
|
+
});
|
|
511
|
+
it('should return empty stats for non-existent file', () => {
|
|
512
|
+
const result = readJsonlSyncWithStats(path.join(testDir, 'nonexistent.jsonl'));
|
|
513
|
+
assert.deepStrictEqual(result.data, []);
|
|
514
|
+
assert.strictEqual(result.stats.totalLines, 0);
|
|
515
|
+
assert.strictEqual(result.stats.parsedLines, 0);
|
|
516
|
+
});
|
|
517
|
+
it('should track all stats together', () => {
|
|
518
|
+
const filePath = path.join(testDir, 'test.jsonl');
|
|
519
|
+
fs.writeFileSync(filePath, '{"id":1}\n\n{bad}\n{"id":2}\n \n{invalid}\n{"id":3}');
|
|
520
|
+
const result = readJsonlSyncWithStats(filePath);
|
|
521
|
+
assert.strictEqual(result.data.length, 3);
|
|
522
|
+
assert.strictEqual(result.stats.totalLines, 7);
|
|
523
|
+
assert.strictEqual(result.stats.parsedLines, 3);
|
|
524
|
+
assert.strictEqual(result.stats.skippedLines, 2);
|
|
525
|
+
assert.strictEqual(result.stats.emptyLines, 2);
|
|
526
|
+
});
|
|
527
|
+
});
|
|
528
|
+
describe('cleanupOldFiles', () => {
|
|
529
|
+
it('should return empty result for non-existent directory', () => {
|
|
530
|
+
const result = cleanupOldFiles(path.join(testDir, 'nonexistent'));
|
|
531
|
+
assert.strictEqual(result.deleted, 0);
|
|
532
|
+
assert.deepStrictEqual(result.errors, []);
|
|
533
|
+
});
|
|
534
|
+
it('should delete files older than retention period', () => {
|
|
535
|
+
// Create files with dates in the past
|
|
536
|
+
const oldDate = '2020-01-01';
|
|
537
|
+
const recentDate = new Date().toISOString().split('T')[0];
|
|
538
|
+
fs.writeFileSync(path.join(testDir, `traces-${oldDate}.jsonl`), '{}');
|
|
539
|
+
fs.writeFileSync(path.join(testDir, `logs-${oldDate}.jsonl`), '{}');
|
|
540
|
+
fs.writeFileSync(path.join(testDir, `traces-${recentDate}.jsonl`), '{}');
|
|
541
|
+
const result = cleanupOldFiles(testDir, 7);
|
|
542
|
+
assert.strictEqual(result.deleted, 2);
|
|
543
|
+
assert.deepStrictEqual(result.errors, []);
|
|
544
|
+
// Verify old files are deleted
|
|
545
|
+
assert.strictEqual(fs.existsSync(path.join(testDir, `traces-${oldDate}.jsonl`)), false);
|
|
546
|
+
assert.strictEqual(fs.existsSync(path.join(testDir, `logs-${oldDate}.jsonl`)), false);
|
|
547
|
+
// Verify recent file is kept
|
|
548
|
+
assert.strictEqual(fs.existsSync(path.join(testDir, `traces-${recentDate}.jsonl`)), true);
|
|
549
|
+
});
|
|
550
|
+
it('should not delete files within retention period', () => {
|
|
551
|
+
const today = new Date();
|
|
552
|
+
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000);
|
|
553
|
+
const yesterdayStr = yesterday.toISOString().split('T')[0];
|
|
554
|
+
fs.writeFileSync(path.join(testDir, `traces-${yesterdayStr}.jsonl`), '{}');
|
|
555
|
+
const result = cleanupOldFiles(testDir, 7);
|
|
556
|
+
assert.strictEqual(result.deleted, 0);
|
|
557
|
+
assert.strictEqual(fs.existsSync(path.join(testDir, `traces-${yesterdayStr}.jsonl`)), true);
|
|
558
|
+
});
|
|
559
|
+
it('should skip files without parseable dates', () => {
|
|
560
|
+
fs.writeFileSync(path.join(testDir, 'some-random-file.jsonl'), '{}');
|
|
561
|
+
fs.writeFileSync(path.join(testDir, 'no-date.jsonl'), '{}');
|
|
562
|
+
const result = cleanupOldFiles(testDir, 7);
|
|
563
|
+
assert.strictEqual(result.deleted, 0);
|
|
564
|
+
assert.strictEqual(fs.existsSync(path.join(testDir, 'some-random-file.jsonl')), true);
|
|
565
|
+
assert.strictEqual(fs.existsSync(path.join(testDir, 'no-date.jsonl')), true);
|
|
566
|
+
});
|
|
567
|
+
it('should only process .jsonl files', () => {
|
|
568
|
+
const oldDate = '2020-01-01';
|
|
569
|
+
fs.writeFileSync(path.join(testDir, `traces-${oldDate}.jsonl`), '{}');
|
|
570
|
+
fs.writeFileSync(path.join(testDir, `traces-${oldDate}.json`), '{}');
|
|
571
|
+
fs.writeFileSync(path.join(testDir, `traces-${oldDate}.txt`), 'text');
|
|
572
|
+
const result = cleanupOldFiles(testDir, 7);
|
|
573
|
+
assert.strictEqual(result.deleted, 1);
|
|
574
|
+
// Only .jsonl file should be deleted
|
|
575
|
+
assert.strictEqual(fs.existsSync(path.join(testDir, `traces-${oldDate}.jsonl`)), false);
|
|
576
|
+
assert.strictEqual(fs.existsSync(path.join(testDir, `traces-${oldDate}.json`)), true);
|
|
577
|
+
assert.strictEqual(fs.existsSync(path.join(testDir, `traces-${oldDate}.txt`)), true);
|
|
578
|
+
});
|
|
579
|
+
it('should respect custom retention period', () => {
|
|
580
|
+
const today = new Date();
|
|
581
|
+
// Create file from 5 days ago
|
|
582
|
+
const fiveDaysAgo = new Date(today.getTime() - 5 * 24 * 60 * 60 * 1000);
|
|
583
|
+
const fiveDaysAgoStr = fiveDaysAgo.toISOString().split('T')[0];
|
|
584
|
+
fs.writeFileSync(path.join(testDir, `traces-${fiveDaysAgoStr}.jsonl`), '{}');
|
|
585
|
+
// With 7 day retention, file should be kept
|
|
586
|
+
let result = cleanupOldFiles(testDir, 7);
|
|
587
|
+
assert.strictEqual(result.deleted, 0);
|
|
588
|
+
assert.strictEqual(fs.existsSync(path.join(testDir, `traces-${fiveDaysAgoStr}.jsonl`)), true);
|
|
589
|
+
// With 3 day retention, file should be deleted
|
|
590
|
+
result = cleanupOldFiles(testDir, 3);
|
|
591
|
+
assert.strictEqual(result.deleted, 1);
|
|
592
|
+
assert.strictEqual(fs.existsSync(path.join(testDir, `traces-${fiveDaysAgoStr}.jsonl`)), false);
|
|
593
|
+
});
|
|
594
|
+
it('should handle empty directory', () => {
|
|
595
|
+
const result = cleanupOldFiles(testDir, 7);
|
|
596
|
+
assert.strictEqual(result.deleted, 0);
|
|
597
|
+
assert.deepStrictEqual(result.errors, []);
|
|
598
|
+
});
|
|
599
|
+
it('should reject directories outside allowed paths', () => {
|
|
600
|
+
const outsideDir = fs.mkdtempSync(path.join(os.tmpdir(), 'outside-'));
|
|
601
|
+
try {
|
|
602
|
+
fs.writeFileSync(path.join(outsideDir, 'traces-2020-01-01.jsonl'), '{}');
|
|
603
|
+
const result = cleanupOldFiles(outsideDir, 7, [testDir]);
|
|
604
|
+
assert.strictEqual(result.deleted, 0);
|
|
605
|
+
assert.strictEqual(result.errors.length, 1);
|
|
606
|
+
assert(result.errors[0].includes('not within allowed paths'));
|
|
607
|
+
// File should still exist
|
|
608
|
+
assert.strictEqual(fs.existsSync(path.join(outsideDir, 'traces-2020-01-01.jsonl')), true);
|
|
609
|
+
}
|
|
610
|
+
finally {
|
|
611
|
+
fs.rmSync(outsideDir, { recursive: true, force: true });
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
it('should allow directories within allowed paths', () => {
|
|
615
|
+
const subDir = path.join(testDir, 'subdir');
|
|
616
|
+
fs.mkdirSync(subDir);
|
|
617
|
+
fs.writeFileSync(path.join(subDir, 'traces-2020-01-01.jsonl'), '{}');
|
|
618
|
+
const result = cleanupOldFiles(subDir, 7, [testDir]);
|
|
619
|
+
assert.strictEqual(result.deleted, 1);
|
|
620
|
+
assert.deepStrictEqual(result.errors, []);
|
|
621
|
+
});
|
|
622
|
+
it('should handle file types: traces, logs, metrics', () => {
|
|
623
|
+
const oldDate = '2020-01-01';
|
|
624
|
+
fs.writeFileSync(path.join(testDir, `traces-${oldDate}.jsonl`), '{}');
|
|
625
|
+
fs.writeFileSync(path.join(testDir, `logs-${oldDate}.jsonl`), '{}');
|
|
626
|
+
fs.writeFileSync(path.join(testDir, `metrics-${oldDate}.jsonl`), '{}');
|
|
627
|
+
const result = cleanupOldFiles(testDir, 7);
|
|
628
|
+
assert.strictEqual(result.deleted, 3);
|
|
629
|
+
assert.deepStrictEqual(result.errors, []);
|
|
630
|
+
});
|
|
631
|
+
it('should use default retention of 7 days', () => {
|
|
632
|
+
const today = new Date();
|
|
633
|
+
// Create file from 8 days ago
|
|
634
|
+
const eightDaysAgo = new Date(today.getTime() - 8 * 24 * 60 * 60 * 1000);
|
|
635
|
+
const eightDaysAgoStr = eightDaysAgo.toISOString().split('T')[0];
|
|
636
|
+
// Create file from 6 days ago
|
|
637
|
+
const sixDaysAgo = new Date(today.getTime() - 6 * 24 * 60 * 60 * 1000);
|
|
638
|
+
const sixDaysAgoStr = sixDaysAgo.toISOString().split('T')[0];
|
|
639
|
+
fs.writeFileSync(path.join(testDir, `traces-${eightDaysAgoStr}.jsonl`), '{}');
|
|
640
|
+
fs.writeFileSync(path.join(testDir, `traces-${sixDaysAgoStr}.jsonl`), '{}');
|
|
641
|
+
// Use default retention (7 days)
|
|
642
|
+
const result = cleanupOldFiles(testDir);
|
|
643
|
+
assert.strictEqual(result.deleted, 1);
|
|
644
|
+
assert.strictEqual(fs.existsSync(path.join(testDir, `traces-${eightDaysAgoStr}.jsonl`)), false);
|
|
645
|
+
assert.strictEqual(fs.existsSync(path.join(testDir, `traces-${sixDaysAgoStr}.jsonl`)), true);
|
|
646
|
+
});
|
|
647
|
+
});
|
|
648
|
+
});
|
|
649
|
+
//# sourceMappingURL=file-utils.test.js.map
|