test: add campaign queue coverage

This commit is contained in:
Cauê Faleiros
2026-05-28 11:18:45 -03:00
parent e2d0e94080
commit 3da299a8af
4 changed files with 206 additions and 85 deletions

View File

@@ -1,5 +1,12 @@
const { pool } = require('../db');
const { N8N_WHATSAPP_TRIGGER_URL } = require('../config');
const {
buildWhatsappCampaignPayload,
formatProductList,
groupCampaignRows,
groupCampaignRowsByBaseProduct,
mapCampaignProducts
} = require('./campaignFormatter');
const TOP_BUYERS_LIMIT = 100;
const MAX_CAMPAIGN_ATTEMPTS = 3;
@@ -109,37 +116,6 @@ const getCampaignQueueRows = async () => {
return result.rows;
};
const groupCampaignRows = (rows) => {
return Object.values(rows.reduce((acc, row) => {
const key = `${row.base_product_name}:${row.status}`;
if (!acc[key]) {
acc[key] = {
key,
baseProductName: row.base_product_name,
status: row.status,
totalDelta: 0,
rowCount: 0,
attempts: 0,
lastError: null,
createdAt: row.created_at,
updatedAt: row.updated_at,
sentAt: row.sent_at,
items: []
};
}
acc[key].totalDelta += Number(row.delta_estoque || 0);
acc[key].rowCount += 1;
acc[key].attempts = Math.max(acc[key].attempts, Number(row.attempts || 0));
acc[key].lastError = row.last_error || acc[key].lastError;
acc[key].createdAt = new Date(row.created_at) < new Date(acc[key].createdAt) ? row.created_at : acc[key].createdAt;
acc[key].updatedAt = new Date(row.updated_at) > new Date(acc[key].updatedAt) ? row.updated_at : acc[key].updatedAt;
acc[key].sentAt = row.sent_at || acc[key].sentAt;
acc[key].items.push(row);
return acc;
}, {})).sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
};
const getCampaignQueueSummary = async () => {
const rows = await getCampaignQueueRows();
return {
@@ -158,11 +134,7 @@ const getCampaignPreview = async () => {
AND attempts < $1
ORDER BY created_at ASC, id ASC;
`, [MAX_CAMPAIGN_ATTEMPTS]);
const groups = result.rows.reduce((acc, row) => {
if (!acc[row.base_product_name]) acc[row.base_product_name] = [];
acc[row.base_product_name].push(row);
return acc;
}, {});
const groups = groupCampaignRowsByBaseProduct(result.rows);
const readyGroups = {};
const belowThresholdGroups = {};
@@ -233,53 +205,11 @@ const updateCampaignItemsStatus = async (ids, status, errorMessage = null) => {
`, [status, errorMessage, ids]);
};
const formatProductList = (productNames) => {
if (productNames.length <= 2) {
return productNames.join(' e ');
}
return `${productNames.slice(0, -1).join(', ')} e ${productNames[productNames.length - 1]}`;
};
const mapCampaignProducts = (groups) => {
return Object.entries(groups)
.sort(([, aItems], [, bItems]) => {
return new Date(aItems[0].created_at).getTime() - new Date(bItems[0].created_at).getTime();
})
.map(([baseProductName, items]) => {
const sortedItems = [...items].sort((a, b) => String(a.nome).localeCompare(String(b.nome), 'pt-BR'));
return {
baseProduct: baseProductName,
total_delta: sortedItems.reduce((sum, item) => sum + Number(item.delta_estoque || 0), 0),
sizes: sortedItems.map(item => ({
id: item.produto_id,
nome: item.nome,
delta: item.delta_estoque,
saldo: item.saldo
})),
itemIds: sortedItems.map(item => item.id)
};
});
};
const sendWhatsappCampaign = async (products, customers) => {
const productNames = products.map(product => product.baseProduct);
const productsText = formatProductList(productNames);
const allSizes = products.flatMap(product => product.sizes);
const totalDelta = products.reduce((sum, product) => sum + product.total_delta, 0);
const response = await fetch(N8N_WHATSAPP_TRIGGER_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
baseProduct: productsText,
productsText,
total_delta: totalDelta,
sizes: allSizes,
products: products.map(({ itemIds, ...product }) => product),
customers
})
body: JSON.stringify(buildWhatsappCampaignPayload(products, customers))
});
if (!response.ok) {
@@ -301,11 +231,7 @@ const processPendingStockCampaigns = async () => {
return summary;
}
const groups = rows.reduce((acc, row) => {
if (!acc[row.base_product_name]) acc[row.base_product_name] = [];
acc[row.base_product_name].push(row);
return acc;
}, {});
const groups = groupCampaignRowsByBaseProduct(rows);
const products = mapCampaignProducts(groups);
const ids = products.flatMap(product => product.itemIds);
const customers = await getTopBuyersAllTime();