Rollback analytics to ed1f129b07
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m5s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m5s
This commit is contained in:
@@ -9,26 +9,10 @@ const {
|
||||
getRfmAnalytics,
|
||||
getRfmSegment,
|
||||
normalizeDateParam,
|
||||
ORDER_DATE_SQL,
|
||||
scoreTertile
|
||||
} = require('../services/analyticsService');
|
||||
const { pool } = require('../db');
|
||||
|
||||
const compactSql = (sql) => sql.replace(/\s+/g, ' ').trim();
|
||||
const expectedDateFilter = ({ startPlaceholder, endPlaceholder } = {}) => {
|
||||
const filters = [`${ORDER_DATE_SQL} IS NOT NULL`];
|
||||
|
||||
if (startPlaceholder) {
|
||||
filters.push(`${ORDER_DATE_SQL} >= ${startPlaceholder}::date`);
|
||||
}
|
||||
|
||||
if (endPlaceholder) {
|
||||
filters.push(`${ORDER_DATE_SQL} <= ${endPlaceholder}::date`);
|
||||
}
|
||||
|
||||
return `WHERE ${filters.join(' AND ')}`;
|
||||
};
|
||||
|
||||
test('normalizeDateParam accepts strict ISO dates', () => {
|
||||
assert.equal(normalizeDateParam('2026-05-28'), '2026-05-28');
|
||||
});
|
||||
@@ -44,8 +28,8 @@ test('buildDateFilter builds bounded date predicates', () => {
|
||||
|
||||
assert.deepEqual(filter.params, ['2026-05-01', '2026-05-28']);
|
||||
assert.equal(
|
||||
compactSql(filter.whereClause),
|
||||
compactSql(expectedDateFilter({ startPlaceholder: '$1', endPlaceholder: '$2' }))
|
||||
filter.whereClause,
|
||||
'WHERE data_pedido_date IS NOT NULL AND data_pedido_date >= $1::date AND data_pedido_date <= $2::date'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -54,8 +38,8 @@ test('buildDateFilter builds Hoje as an inclusive single-day date predicate', ()
|
||||
|
||||
assert.deepEqual(filter.params, ['2026-06-15', '2026-06-15']);
|
||||
assert.equal(
|
||||
compactSql(filter.whereClause),
|
||||
compactSql(expectedDateFilter({ startPlaceholder: '$1', endPlaceholder: '$2' }))
|
||||
filter.whereClause,
|
||||
'WHERE data_pedido_date IS NOT NULL AND data_pedido_date >= $1::date AND data_pedido_date <= $2::date'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -64,8 +48,8 @@ test('buildDateFilter builds Ontem as an inclusive single-day date predicate', (
|
||||
|
||||
assert.deepEqual(filter.params, ['2026-06-14', '2026-06-14']);
|
||||
assert.equal(
|
||||
compactSql(filter.whereClause),
|
||||
compactSql(expectedDateFilter({ startPlaceholder: '$1', endPlaceholder: '$2' }))
|
||||
filter.whereClause,
|
||||
'WHERE data_pedido_date IS NOT NULL AND data_pedido_date >= $1::date AND data_pedido_date <= $2::date'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -74,8 +58,8 @@ test('buildDateFilter builds Ultimos 7 dias as an inclusive calendar range', ()
|
||||
|
||||
assert.deepEqual(filter.params, ['2026-06-09', '2026-06-15']);
|
||||
assert.equal(
|
||||
compactSql(filter.whereClause),
|
||||
compactSql(expectedDateFilter({ startPlaceholder: '$1', endPlaceholder: '$2' }))
|
||||
filter.whereClause,
|
||||
'WHERE data_pedido_date IS NOT NULL AND data_pedido_date >= $1::date AND data_pedido_date <= $2::date'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -84,8 +68,8 @@ test('buildDateFilter builds custom single-day ranges inclusively', () => {
|
||||
|
||||
assert.deepEqual(filter.params, ['2026-06-10', '2026-06-10']);
|
||||
assert.equal(
|
||||
compactSql(filter.whereClause),
|
||||
compactSql(expectedDateFilter({ startPlaceholder: '$1', endPlaceholder: '$2' }))
|
||||
filter.whereClause,
|
||||
'WHERE data_pedido_date IS NOT NULL AND data_pedido_date >= $1::date AND data_pedido_date <= $2::date'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -94,20 +78,11 @@ test('buildDateFilter ignores invalid bounds', () => {
|
||||
|
||||
assert.deepEqual(filter.params, ['2026-05-28']);
|
||||
assert.equal(
|
||||
compactSql(filter.whereClause),
|
||||
compactSql(expectedDateFilter({ endPlaceholder: '$1' }))
|
||||
filter.whereClause,
|
||||
'WHERE data_pedido_date IS NOT NULL AND data_pedido_date <= $1::date'
|
||||
);
|
||||
});
|
||||
|
||||
test('buildDateFilter falls back to parsing the stored display date when normalized dates are missing', () => {
|
||||
const filter = buildDateFilter({ start: '2026-05-01', end: '2026-05-28' });
|
||||
const whereClause = compactSql(filter.whereClause);
|
||||
|
||||
assert.match(whereClause, /COALESCE\( data_pedido_date,/);
|
||||
assert.match(whereClause, /data_pedido ~ '\^\\d\{4\}/);
|
||||
assert.match(whereClause, /data_pedido ~ '\^\\d\{1,2\}/);
|
||||
});
|
||||
|
||||
test('getPreviousDate returns the calendar day before an ISO date', () => {
|
||||
assert.equal(getPreviousDate('2026-06-15'), '2026-06-14');
|
||||
assert.equal(getPreviousDate('2026-03-01'), '2026-02-28');
|
||||
@@ -216,30 +191,28 @@ test('buildRfmClients scores segments from RFM history when period totals are sm
|
||||
assert.equal(yesterdayBuyer.monetaryScore, 3);
|
||||
});
|
||||
|
||||
test('getRfmAnalytics classifies period buyers by history through the selected range end', async () => {
|
||||
test('getRfmAnalytics groups period buyers by their RFM tag before the selected period', async () => {
|
||||
const originalQuery = pool.query;
|
||||
const calls = [];
|
||||
|
||||
pool.query = async (sql, params) => {
|
||||
calls.push({ sql, params });
|
||||
const isHistoryQuery = params.length === 1;
|
||||
const referenceDate = params[0];
|
||||
const isHistoryQuery = sql.includes('recency_days');
|
||||
const endDate = params[0];
|
||||
|
||||
if (isHistoryQuery) {
|
||||
return {
|
||||
rows: [
|
||||
{
|
||||
customer_key: '1',
|
||||
name: 'Cliente Ontem',
|
||||
phone: '1',
|
||||
monetary: 5000,
|
||||
frequency: 20,
|
||||
quantity_purchased: 20,
|
||||
last_purchase_date: '2026-06-14',
|
||||
recency_days: referenceDate === '2026-06-14' ? 0 : 1
|
||||
recency_days: endDate === '2026-06-13' ? 0 : 1
|
||||
},
|
||||
{
|
||||
customer_key: '2',
|
||||
name: 'Cliente Antigo',
|
||||
phone: '2',
|
||||
monetary: 50,
|
||||
@@ -249,7 +222,6 @@ test('getRfmAnalytics classifies period buyers by history through the selected r
|
||||
recency_days: 164
|
||||
},
|
||||
{
|
||||
customer_key: '3',
|
||||
name: 'Cliente Medio',
|
||||
phone: '3',
|
||||
monetary: 100,
|
||||
@@ -257,16 +229,6 @@ test('getRfmAnalytics classifies period buyers by history through the selected r
|
||||
quantity_purchased: 2,
|
||||
last_purchase_date: '2026-03-01',
|
||||
recency_days: 105
|
||||
},
|
||||
{
|
||||
customer_key: 'name:Cliente Sem Fone',
|
||||
name: 'Cliente Sem Fone',
|
||||
phone: null,
|
||||
monetary: 1000,
|
||||
frequency: 10,
|
||||
quantity_purchased: 10,
|
||||
last_purchase_date: '2026-06-14',
|
||||
recency_days: 1
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -275,7 +237,6 @@ test('getRfmAnalytics classifies period buyers by history through the selected r
|
||||
return {
|
||||
rows: [
|
||||
{
|
||||
customer_key: '1',
|
||||
name: 'Cliente Ontem',
|
||||
phone: '1',
|
||||
monetary: 100,
|
||||
@@ -284,22 +245,12 @@ test('getRfmAnalytics classifies period buyers by history through the selected r
|
||||
last_purchase_date: '2026-06-14'
|
||||
},
|
||||
{
|
||||
customer_key: '4',
|
||||
name: 'Cliente Novo no Periodo',
|
||||
phone: '4',
|
||||
monetary: 25,
|
||||
frequency: 1,
|
||||
quantity_purchased: 1,
|
||||
last_purchase_date: '2026-06-14'
|
||||
},
|
||||
{
|
||||
customer_key: 'name:Cliente Sem Fone',
|
||||
name: 'Cliente Sem Fone',
|
||||
phone: null,
|
||||
monetary: 30,
|
||||
frequency: 1,
|
||||
quantity_purchased: 1,
|
||||
last_purchase_date: '2026-06-14'
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -310,69 +261,18 @@ test('getRfmAnalytics classifies period buyers by history through the selected r
|
||||
const yesterday = await getRfmAnalytics({ start: '2026-06-14', end: '2026-06-14' });
|
||||
|
||||
assert.deepEqual(calls[0].params, ['2026-06-09', '2026-06-15']);
|
||||
assert.match(compactSql(calls[1].sql), /\(\$1::date - MAX\( COALESCE\( data_pedido_date,/);
|
||||
assert.match(compactSql(calls[1].sql), /COALESCE\( data_pedido_date,.* <= \$1::date/);
|
||||
assert.deepEqual(calls[1].params, ['2026-06-15']);
|
||||
assert.match(calls[1].sql, /\(\$1::date - MAX\(data_pedido_date\)\)::int/);
|
||||
assert.match(calls[1].sql, /data_pedido_date <= \$1::date/);
|
||||
assert.deepEqual(calls[1].params, ['2026-06-08']);
|
||||
assert.deepEqual(calls[2].params, ['2026-06-14', '2026-06-14']);
|
||||
assert.deepEqual(calls[3].params, ['2026-06-14']);
|
||||
assert.deepEqual(calls[3].params, ['2026-06-13']);
|
||||
assert.equal(sevenDays.clients[0].segmentKey, 'champions');
|
||||
assert.equal(yesterday.clients[0].segmentKey, 'champions');
|
||||
assert.equal(yesterday.clients[0].frequency, 1);
|
||||
assert.equal(yesterday.clients[0].monetary, 100);
|
||||
assert.equal(yesterday.clients.length, 3);
|
||||
assert.equal(yesterday.clients.length, 2);
|
||||
assert.ok(!yesterday.clients.some(client => client.phone === '2'));
|
||||
assert.ok(yesterday.clients.some(client => client.phone === '4' && client.segmentKey === 'new_customers'));
|
||||
assert.ok(yesterday.clients.some(client => (
|
||||
client.customerKey === 'name:Cliente Sem Fone' &&
|
||||
client.phone === '' &&
|
||||
client.segmentKey === 'champions' &&
|
||||
client.monetary === 30
|
||||
)));
|
||||
} finally {
|
||||
pool.query = originalQuery;
|
||||
}
|
||||
});
|
||||
|
||||
test('getRfmAnalytics reuses period rows as RFV history for all-period ranges', async () => {
|
||||
const originalQuery = pool.query;
|
||||
const calls = [];
|
||||
|
||||
pool.query = async (sql, params) => {
|
||||
calls.push({ sql, params });
|
||||
return {
|
||||
rows: [
|
||||
{
|
||||
customer_key: '1',
|
||||
name: 'Cliente Frequente',
|
||||
phone: '1',
|
||||
monetary: 5000,
|
||||
frequency: 20,
|
||||
quantity_purchased: 20,
|
||||
last_purchase_date: '2026-06-14',
|
||||
recency_days: 1
|
||||
},
|
||||
{
|
||||
customer_key: '2',
|
||||
name: 'Cliente Antigo',
|
||||
phone: '2',
|
||||
monetary: 50,
|
||||
frequency: 1,
|
||||
quantity_purchased: 1,
|
||||
last_purchase_date: '2026-01-01',
|
||||
recency_days: 165
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await getRfmAnalytics({ start: '2000-01-01', end: '2026-06-15' });
|
||||
|
||||
assert.equal(calls.length, 1);
|
||||
assert.match(compactSql(calls[0].sql), /\(\$2::date - MAX\( COALESCE\( data_pedido_date,/);
|
||||
assert.deepEqual(calls[0].params, ['2000-01-01', '2026-06-15']);
|
||||
assert.equal(result.clients.length, 2);
|
||||
assert.equal(result.segments.reduce((total, segment) => total + segment.count, 0), 2);
|
||||
} finally {
|
||||
pool.query = originalQuery;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user