164 lines
4.5 KiB
JavaScript
164 lines
4.5 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const http = require('node:http');
|
|
const test = require('node:test');
|
|
|
|
const { closeDbPool, getDb } = require('../src/config/db');
|
|
const { syncOrderStatuses } = require('../src/services/orders.service');
|
|
|
|
const createdUserIds = [];
|
|
const createdServiceIds = [];
|
|
let providerMock;
|
|
const statuses = new Map();
|
|
|
|
function unique(prefix) {
|
|
return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
}
|
|
|
|
function startProviderMock() {
|
|
const server = http.createServer(async (req, res) => {
|
|
const chunks = [];
|
|
|
|
for await (const chunk of req) {
|
|
chunks.push(chunk);
|
|
}
|
|
|
|
const body = new URLSearchParams(Buffer.concat(chunks).toString());
|
|
const orderId = body.get('order');
|
|
const payload = statuses.get(orderId) || { status: 'Processing', remains: 10 };
|
|
|
|
res.setHeader('content-type', 'application/json');
|
|
|
|
if (body.get('action') !== 'status') {
|
|
res.end(JSON.stringify({ error: 'unsupported action' }));
|
|
return;
|
|
}
|
|
|
|
res.end(JSON.stringify(payload));
|
|
});
|
|
|
|
return new Promise((resolve) => {
|
|
server.listen(0, '127.0.0.1', () => {
|
|
resolve({
|
|
url: `http://127.0.0.1:${server.address().port}`,
|
|
close: () => new Promise((done) => server.close(done))
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
async function createUser(balance = '0.0000') {
|
|
const username = unique('status_user');
|
|
const [id] = await getDb()('users').insert({
|
|
username,
|
|
email: `${username}@example.com`,
|
|
password_hash: 'hash',
|
|
balance
|
|
});
|
|
|
|
createdUserIds.push(id);
|
|
return id;
|
|
}
|
|
|
|
async function createService() {
|
|
const [id] = await getDb()('services').insert({
|
|
provider_service_id: unique('status_provider_service'),
|
|
name: 'Status Test Service',
|
|
category: 'Status',
|
|
provider_price: '2.0000',
|
|
markup_percent: '25.0000',
|
|
sell_price: '2.5000',
|
|
min: 10,
|
|
max: 10000,
|
|
is_active: true
|
|
});
|
|
|
|
createdServiceIds.push(id);
|
|
return id;
|
|
}
|
|
|
|
async function createTrackedOrder(userId, serviceId, providerOrderId, status = 'processing') {
|
|
const [id] = await getDb()('orders').insert({
|
|
user_id: userId,
|
|
service_id: serviceId,
|
|
link: 'https://example.com/status-order',
|
|
quantity: 1000,
|
|
charge: '2.5000',
|
|
provider_order_id: providerOrderId,
|
|
status
|
|
});
|
|
|
|
return id;
|
|
}
|
|
|
|
test.before(async () => {
|
|
providerMock = await startProviderMock();
|
|
process.env.PROVIDER_API_URL = providerMock.url;
|
|
process.env.PROVIDER_API_KEY = 'mock-key';
|
|
});
|
|
|
|
test.after(async () => {
|
|
const db = getDb();
|
|
|
|
if (createdUserIds.length > 0) {
|
|
await db('orders').whereIn('user_id', createdUserIds).del();
|
|
await db('transactions').whereIn('user_id', createdUserIds).del();
|
|
await db('users').whereIn('id', createdUserIds).del();
|
|
}
|
|
|
|
if (createdServiceIds.length > 0) {
|
|
await db('services').whereIn('id', createdServiceIds).del();
|
|
}
|
|
|
|
await closeDbPool();
|
|
await providerMock.close();
|
|
});
|
|
|
|
test('syncOrderStatuses updates completed orders with remains and provider charge', async () => {
|
|
const userId = await createUser();
|
|
const serviceId = await createService();
|
|
const providerOrderId = unique('provider_completed');
|
|
const orderId = await createTrackedOrder(userId, serviceId, providerOrderId);
|
|
|
|
statuses.set(providerOrderId, {
|
|
status: 'Completed',
|
|
charge: '1.7500',
|
|
start_count: 100,
|
|
remains: 0
|
|
});
|
|
|
|
const result = await syncOrderStatuses();
|
|
const order = await getDb()('orders').where({ id: orderId }).first();
|
|
|
|
assert.equal(result.updated >= 1, true);
|
|
assert.equal(order.status, 'completed');
|
|
assert.equal(order.start_count, 100);
|
|
assert.equal(order.remains, 0);
|
|
assert.equal(order.provider_charge, '1.7500');
|
|
assert.ok(order.last_status_check_at);
|
|
});
|
|
|
|
test('partial status is recorded without automatic wallet refund', async () => {
|
|
const userId = await createUser('7.5000');
|
|
const serviceId = await createService();
|
|
const providerOrderId = unique('provider_partial');
|
|
const orderId = await createTrackedOrder(userId, serviceId, providerOrderId);
|
|
|
|
statuses.set(providerOrderId, {
|
|
status: 'Partial',
|
|
charge: '1.0000',
|
|
start_count: 55,
|
|
remains: 400
|
|
});
|
|
|
|
await syncOrderStatuses();
|
|
|
|
const order = await getDb()('orders').where({ id: orderId }).first();
|
|
const user = await getDb()('users').where({ id: userId }).first();
|
|
const refunds = await getDb()('transactions').where({ user_id: userId, type: 'refund' });
|
|
|
|
assert.equal(order.status, 'partial');
|
|
assert.equal(order.remains, 400);
|
|
assert.equal(user.balance, '7.5000');
|
|
assert.equal(refunds.length, 0);
|
|
});
|