1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/* Bot tests with stubbed Telegram + gateway (no network). Run: node --test test/bot.test.js */
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { parseCommand, findUser, handleUpdate } from '../src/bot.js';
test('parseCommand', () => {
assert.deepEqual(parseCommand('/newuser مامان 20 30'), { cmd: 'newuser', args: ['مامان', '20', '30'] });
assert.deepEqual(parseCommand('/users@mybot'), { cmd: 'users', args: [] });
assert.equal(parseCommand('hello'), null);
});
test('findUser', () => {
const users = [
{ uuid: 'a1b2c3d4-0000-0000-0000-000000000001', name: 'مامان' },
{ uuid: 'a1b2c3d4-0000-0000-0000-000000000002', name: 'بابا' },
];
assert.equal(findUser(users, 'مامان').length, 1);
assert.equal(findUser(users, 'a1b2c3d4-0000').length, 2);
assert.equal(findUser(users, 'xyz').length, 0);
});
function stubDeps(over = {}) {
const sent = [], docs = [];
const users = [{ uuid: '11111111-2222-3333-4444-555555555555', name: 'مامان', quotaBytes: 21474836480, used_up: 0, used_down: 0, usedUp: 100, usedDown: 200, usedTotal: 300, expiresAt: 0, revoked: false }];
const deps = {
env: { ADMIN_CHAT_ID: '123', GATEWAY_URL: 'https://gw.example.com', ...(over.env || {}) },
send: async (chatId, text) => { sent.push({ chatId, text }); },
tg: async (method, body) => { docs.push({ method, body }); return {}; },
gw: async (path, method, body) => {
if (path === '/admin/users' && method === 'POST') return { ...users[0], uuid: '99999999-2222-3333-4444-555555555555', name: body.name };
if (path === '/admin/users') return users;
if (path === '/admin/backup') return { users };
return {};
},
...over.deps,
};
return { deps, sent, docs };
}
const upd = (text, chat = 123) => ({ message: { chat: { id: chat }, text } });
test('rejects non-admin chat', async () => {
const { deps, sent } = stubDeps();
await handleUpdate(upd('/users', 999), deps);
assert.match(sent[0].text, /خصوصیه/);
assert.match(sent[0].text, /999/);
});
test('no ADMIN_CHAT_ID → setup hint', async () => {
const { deps, sent } = stubDeps({ env: { ADMIN_CHAT_ID: '' } });
await handleUpdate(upd('/start', 777), deps);
assert.match(sent[0].text, /ADMIN_CHAT_ID/);
});
test('/newuser creates and replies link', async () => {
const { deps, sent } = stubDeps();
await handleUpdate(upd('/newuser مامان 20 30'), deps);
assert.match(sent[0].text, /ساخته شد/);
assert.match(sent[0].text, /vless:\/\/99999999-2222-3333-4444-555555555555@gw\.example\.com/);
});
test('/users lists', async () => {
const { deps, sent } = stubDeps();
await handleUpdate(upd('/users'), deps);
assert.match(sent[0].text, /مامان/);
});
test('/usage shows detail', async () => {
const { deps, sent } = stubDeps();
await handleUpdate(upd('/usage مامان'), deps);
assert.match(sent[0].text, /مصرف/);
});
test('/health', async () => {
const { deps, sent } = stubDeps();
await handleUpdate(upd('/health'), deps);
assert.match(sent[0].text, /گیتوی: ✅/);
});
test('/backup sends document', async () => {
const { deps, docs } = stubDeps();
await handleUpdate(upd('/backup'), deps);
assert.equal(docs[0].method, 'sendDocument');
});
test('unknown command', async () => {
const { deps, sent } = stubDeps();
await handleUpdate(upd('/blah'), deps);
assert.match(sent[0].text, /نفهمیدم/);
});
test('non-command ignored', async () => {
const { deps, sent } = stubDeps();
await handleUpdate(upd('سلام'), deps);
assert.equal(sent.length, 0);
});