105 lines
3.7 KiB
TypeScript
105 lines
3.7 KiB
TypeScript
import fs from 'node:fs/promises';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import { afterEach, describe, expect, it } from 'vitest';
|
|
import type { ProxyUserRecord } from '../../src/shared/contracts';
|
|
import { readObservedTraffic } from './traffic';
|
|
|
|
const cleanupDirs: string[] = [];
|
|
|
|
afterEach(async () => {
|
|
await Promise.all(cleanupDirs.splice(0).map((dir) => fs.rm(dir, { recursive: true, force: true })));
|
|
});
|
|
|
|
describe('readObservedTraffic', () => {
|
|
it('derives current user usage, recent activity, and daily totals from 3proxy access logs', async () => {
|
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), '3proxy-traffic-'));
|
|
cleanupDirs.push(dir);
|
|
|
|
const logDir = path.join(dir, 'logs');
|
|
await fs.mkdir(logDir, { recursive: true });
|
|
await fs.writeFile(
|
|
path.join(logDir, '3proxy.log.2026.04.02'),
|
|
[
|
|
'260402111500.000 1080 00000 night-shift 172.19.0.1:54402 104.18.27.120:80 100 900 0 GET http://example.com/ HTTP/1.1',
|
|
'260402115900.000 2080 00000 lab-unlimited 172.19.0.1:53490 8.6.112.0:80 75 842 0 CONNECT example.com:80',
|
|
'260402115930.000 1080 00000 - 0.0.0.0:1080 0.0.0.0:0 0 0 0 Accepting connections [18/1]',
|
|
].join('\n'),
|
|
'utf8',
|
|
);
|
|
await fs.writeFile(
|
|
path.join(logDir, '3proxy.log.2026.04.01'),
|
|
'260401230000.000 1080 00000 night-shift 172.19.0.1:50000 104.18.27.120:80 50 150 0 GET http://example.com/ HTTP/1.1\n',
|
|
'utf8',
|
|
);
|
|
|
|
const users: ProxyUserRecord[] = [
|
|
{
|
|
id: 'u-1',
|
|
username: 'night-shift',
|
|
password: 'secret',
|
|
serviceId: 'socks-main',
|
|
status: 'idle',
|
|
usedBytes: 0,
|
|
quotaBytes: 1024,
|
|
},
|
|
{
|
|
id: 'u-2',
|
|
username: 'lab-unlimited',
|
|
password: 'secret',
|
|
serviceId: 'socks-lab',
|
|
status: 'idle',
|
|
usedBytes: 0,
|
|
quotaBytes: null,
|
|
},
|
|
];
|
|
|
|
const observed = await readObservedTraffic(
|
|
{
|
|
rootDir: dir,
|
|
configPath: path.join(dir, 'generated', '3proxy.cfg'),
|
|
counterPath: path.join(dir, 'state', 'counters.3cf'),
|
|
reportDir: path.join(dir, 'state', 'reports'),
|
|
logPath: path.join(logDir, '3proxy.log'),
|
|
pidPath: path.join(dir, '3proxy.pid'),
|
|
},
|
|
users,
|
|
new Date(2026, 3, 2, 12, 0, 0, 0),
|
|
);
|
|
|
|
expect(observed.totalBytes).toBe(1917);
|
|
expect(observed.liveConnections).toBe(1);
|
|
expect(observed.activeUsers).toBe(2);
|
|
expect(observed.userBytesByName.get('night-shift')).toBe(1000);
|
|
expect(observed.userBytesByName.get('lab-unlimited')).toBe(917);
|
|
expect(observed.recentUsers.has('night-shift')).toBe(false);
|
|
expect(observed.recentUsers.has('lab-unlimited')).toBe(true);
|
|
expect(observed.daily[observed.daily.length - 2].bytes).toBe(200);
|
|
expect(observed.daily[observed.daily.length - 1].bytes).toBe(1917);
|
|
});
|
|
|
|
it('returns zeroed metrics when no runtime logs exist yet', async () => {
|
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), '3proxy-traffic-empty-'));
|
|
cleanupDirs.push(dir);
|
|
|
|
const observed = await readObservedTraffic(
|
|
{
|
|
rootDir: dir,
|
|
configPath: path.join(dir, 'generated', '3proxy.cfg'),
|
|
counterPath: path.join(dir, 'state', 'counters.3cf'),
|
|
reportDir: path.join(dir, 'state', 'reports'),
|
|
logPath: path.join(dir, 'logs', '3proxy.log'),
|
|
pidPath: path.join(dir, '3proxy.pid'),
|
|
},
|
|
[],
|
|
new Date(2026, 3, 2, 12, 0, 0, 0),
|
|
);
|
|
|
|
expect(observed.totalBytes).toBe(0);
|
|
expect(observed.liveConnections).toBe(0);
|
|
expect(observed.activeUsers).toBe(0);
|
|
expect(observed.daily).toHaveLength(5);
|
|
expect(observed.daily.every((entry) => entry.bytes === 0 && entry.share === 0)).toBe(true);
|
|
});
|
|
});
|