const assert = require('node:assert/strict'); const test = require('node:test'); const bcrypt = require('bcryptjs'); const { createApp } = require('../src/app'); const { closeDbPool, getDb } = require('../src/config/db'); const { validateEnv } = require('../src/config/env'); const createdUserIds = []; const createdServiceIds = []; const createdOrderIds = []; const createdPaymentMethodIds = []; function unique(prefix) { return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2)}`; } function listen(app) { return new Promise((resolve) => { const server = app.listen(0, '127.0.0.1', () => { resolve({ baseUrl: `http://127.0.0.1:${server.address().port}`, close: () => new Promise((done) => server.close(done)) }); }); }); } async function request(baseUrl, path, options = {}) { const response = await fetch(`${baseUrl}${path}`, { ...options, headers: { 'content-type': 'application/json', ...(options.headers || {}) } }); const body = await response.json().catch(() => ({})); return { response, body }; } async function createAccount(baseUrl, prefix) { const account = { username: unique(prefix), email: `${unique(prefix)}@example.com`, password: 'StrongPass123' }; const registered = await request(baseUrl, '/auth/register', { method: 'POST', body: JSON.stringify(account) }); assert.equal(registered.response.status, 201); createdUserIds.push(registered.body.user.id); return { ...account, id: registered.body.user.id }; } async function createDbAccount(prefix, overrides = {}) { const db = getDb(); const password = 'StrongPass123'; const account = { username: unique(prefix), email: `${unique(prefix)}@example.com`, password }; const [id] = await db('users').insert({ username: account.username, email: account.email, password_hash: await bcrypt.hash(password, 12), balance: '0.0000', role: 'user', status: 'active', ...overrides }); createdUserIds.push(id); return { ...account, id }; } async function login(baseUrl, account) { const loggedIn = await request(baseUrl, '/auth/login', { method: 'POST', body: JSON.stringify({ email: account.email, password: account.password }) }); assert.equal(loggedIn.response.status, 200); return loggedIn.body.accessToken; } test.after(async () => { const db = getDb(); if (createdOrderIds.length > 0) { await db('orders').whereIn('id', createdOrderIds).del(); } if (createdServiceIds.length > 0) { await db('services').whereIn('id', createdServiceIds).del(); } if (createdPaymentMethodIds.length > 0) { await db('payment_methods').whereIn('id', createdPaymentMethodIds).del(); } if (createdUserIds.length > 0) { await db('password_reset_tokens').whereIn('user_id', createdUserIds).del(); await db('refresh_tokens').whereIn('user_id', createdUserIds).del(); await db('deposits').whereIn('user_id', createdUserIds).orWhereIn('reviewed_by', createdUserIds).del(); await db('transactions').whereIn('user_id', createdUserIds).del(); await db('users').whereIn('id', createdUserIds).del(); } await closeDbPool(); }); test('admin users and stats work, while normal users receive 403', async () => { process.env.JWT_SECRET = process.env.JWT_SECRET || 'test_secret'; const api = await listen(createApp()); try { const db = getDb(); const admin = await createAccount(api.baseUrl, 'admin_test_admin'); const user = await createAccount(api.baseUrl, 'admin_test_user'); await db('users').where({ id: admin.id }).update({ role: 'admin' }); const [serviceId] = await db('services').insert({ provider_service_id: unique('admin_provider_service'), name: 'Admin Test Service', category: 'Admin', provider_price: '4.0000', markup_percent: '25.0000', sell_price: '5.0000', min: 100, max: 10000, is_active: true }); createdServiceIds.push(serviceId); const [orderId] = await db('orders').insert({ user_id: user.id, service_id: serviceId, link: 'https://example.com/admin-order', quantity: 1000, charge: '5.0000', provider_order_id: unique('admin_provider_order'), status: 'completed' }); createdOrderIds.push(orderId); const adminToken = await login(api.baseUrl, admin); const userToken = await login(api.baseUrl, user); const users = await request(api.baseUrl, '/admin/users', { headers: { authorization: `Bearer ${adminToken}` } }); assert.equal(users.response.status, 200); assert.equal(users.body.users.some((row) => Number(row.id) === Number(user.id)), true); const stats = await request(api.baseUrl, '/admin/stats', { headers: { authorization: `Bearer ${adminToken}` } }); assert.equal(stats.response.status, 200); assert.equal(stats.body.stats.gross_sales, '5.0000'); assert.equal(stats.body.stats.provider_cost, '4.0000'); assert.equal(stats.body.stats.profit, '1.0000'); const adjusted = await request(api.baseUrl, `/admin/users/${user.id}`, { method: 'PATCH', headers: { authorization: `Bearer ${adminToken}` }, body: JSON.stringify({ amount: '3.0000', note: 'Admin test credit' }) }); assert.equal(adjusted.response.status, 200); assert.equal(adjusted.body.user.balance, '3.0000'); assert.equal(adjusted.body.wallet.balance, '3.0000'); const banned = await request(api.baseUrl, `/admin/users/${user.id}`, { method: 'PATCH', headers: { authorization: `Bearer ${adminToken}` }, body: JSON.stringify({ status: 'banned' }) }); assert.equal(banned.response.status, 200); assert.equal(banned.body.user.status, 'banned'); const forbiddenChecks = [ request(api.baseUrl, '/admin/users', { headers: { authorization: `Bearer ${userToken}` } }), request(api.baseUrl, '/admin/deposits', { headers: { authorization: `Bearer ${userToken}` } }), request(api.baseUrl, '/admin/orders', { headers: { authorization: `Bearer ${userToken}` } }), request(api.baseUrl, '/admin/services', { headers: { authorization: `Bearer ${userToken}` } }), request(api.baseUrl, '/admin/payment-methods', { headers: { authorization: `Bearer ${userToken}` } }), request(api.baseUrl, '/admin/stats', { headers: { authorization: `Bearer ${userToken}` } }), request(api.baseUrl, `/admin/users/${user.id}`, { method: 'PATCH', headers: { authorization: `Bearer ${userToken}` }, body: JSON.stringify({ status: 'active' }) }), request(api.baseUrl, '/admin/services/sync', { method: 'POST', headers: { authorization: `Bearer ${userToken}` }, body: JSON.stringify({}) }), request(api.baseUrl, '/admin/deposits/1', { method: 'PATCH', headers: { authorization: `Bearer ${userToken}` }, body: JSON.stringify({ action: 'reject' }) }), request(api.baseUrl, `/admin/services/${serviceId}`, { method: 'PATCH', headers: { authorization: `Bearer ${userToken}` }, body: JSON.stringify({ is_active: false }) }), request(api.baseUrl, '/admin/payment-methods', { method: 'POST', headers: { authorization: `Bearer ${userToken}` }, body: JSON.stringify({ code: unique('normal_forbidden'), label_ar: 'Forbidden', instructions: 'Forbidden payment method' }) }) ]; const forbiddenResponses = await Promise.all(forbiddenChecks); assert.equal(forbiddenResponses.every((result) => result.response.status === 403), true); } finally { await api.close(); } }); test('admin can manage payment methods and active list hides disabled methods', async () => { process.env.JWT_SECRET = process.env.JWT_SECRET || 'test_secret'; const api = await listen(createApp()); try { const db = getDb(); const admin = await createAccount(api.baseUrl, 'payment_admin'); const user = await createAccount(api.baseUrl, 'payment_user'); await db('users').where({ id: admin.id }).update({ role: 'admin' }); const adminToken = await login(api.baseUrl, admin); const userToken = await login(api.baseUrl, user); const code = unique('payment_method').replace(/[^a-z0-9_]/g, '_').slice(0, 30); const created = await request(api.baseUrl, '/admin/payment-methods', { method: 'POST', headers: { authorization: `Bearer ${adminToken}` }, body: JSON.stringify({ code, label_ar: 'اختبار دفع', instructions: 'تعليمات قناة الدفع الاختبارية', destination: 'test@example.com', sort_order: 999 }) }); assert.equal(created.response.status, 201); createdPaymentMethodIds.push(created.body.paymentMethod.id); const active = await request(api.baseUrl, '/payment-methods', { headers: { authorization: `Bearer ${userToken}` } }); assert.equal(active.response.status, 200); assert.equal(active.body.paymentMethods.some((method) => method.code === code), true); const disabled = await request(api.baseUrl, `/admin/payment-methods/${created.body.paymentMethod.id}`, { method: 'PATCH', headers: { authorization: `Bearer ${adminToken}` }, body: JSON.stringify({ is_active: false }) }); assert.equal(disabled.response.status, 200); assert.equal(disabled.body.paymentMethod.is_active, 0); const hidden = await request(api.baseUrl, '/payment-methods', { headers: { authorization: `Bearer ${userToken}` } }); assert.equal(hidden.response.status, 200); assert.equal(hidden.body.paymentMethods.some((method) => method.code === code), false); } finally { await api.close(); } }); test('admin user update refuses self-ban and blocks rate-limited sensitive routes', async () => { process.env.JWT_SECRET = process.env.JWT_SECRET || 'test_secret'; const api = await listen(createApp()); try { const admin = await createDbAccount('security_admin', { role: 'admin' }); const user = await createDbAccount('security_user'); const adminToken = await login(api.baseUrl, admin); const userToken = await login(api.baseUrl, user); const selfBan = await request(api.baseUrl, `/admin/users/${admin.id}`, { method: 'PATCH', headers: { authorization: `Bearer ${adminToken}` }, body: JSON.stringify({ status: 'banned' }) }); assert.equal(selfBan.response.status, 400); assert.equal(selfBan.body.error.code, 'ADMIN_SELF_BAN_FORBIDDEN'); const forgotResponses = []; for (let index = 0; index < 6; index += 1) { forgotResponses.push(await request(api.baseUrl, '/auth/forgot-password', { method: 'POST', body: JSON.stringify({ email: `missing_${index}@example.com` }) })); } assert.equal(forgotResponses.slice(0, 5).every((result) => result.response.status === 200), true); assert.equal(forgotResponses[5].response.status, 429); const depositResponses = []; for (let index = 0; index < 11; index += 1) { depositResponses.push(await request(api.baseUrl, '/deposits', { method: 'POST', headers: { authorization: `Bearer ${userToken}` }, body: JSON.stringify({ amount: '1.0000', method: 'paypal', tx_ref: unique(`rate_limited_deposit_${index}`) }) })); } assert.equal(depositResponses.slice(0, 10).every((result) => result.response.status === 201), true); assert.equal(depositResponses[10].response.status, 429); } finally { await api.close(); } }); test('production environment requires CORS_ORIGIN', () => { const previous = { NODE_ENV: process.env.NODE_ENV, DATABASE_URL: process.env.DATABASE_URL, JWT_SECRET: process.env.JWT_SECRET, PROVIDER_API_URL: process.env.PROVIDER_API_URL, PROVIDER_API_KEY: process.env.PROVIDER_API_KEY, CORS_ORIGIN: process.env.CORS_ORIGIN }; try { process.env.NODE_ENV = 'production'; process.env.DATABASE_URL = 'mysql://smm:smm_password@127.0.0.1:3306/smm'; process.env.JWT_SECRET = 'a'.repeat(32); process.env.PROVIDER_API_URL = 'https://provider.real/api/v2'; process.env.PROVIDER_API_KEY = 'real_provider_key'; delete process.env.CORS_ORIGIN; assert.throws( () => validateEnv(), /Missing required environment variables: CORS_ORIGIN/ ); process.env.CORS_ORIGIN = 'https://panel.example.com'; assert.doesNotThrow(() => validateEnv()); } finally { Object.entries(previous).forEach(([key, value]) => { if (value === undefined) { delete process.env[key]; } else { process.env[key] = value; } }); } });