Fix: stamp semana_iniciada_em when campaign first transitions to 'rodando'

Previously, setCampaignStatus did not set semana_iniciada_em, causing
escalateWeeklyLimits to bail early (if (!campaign.semana_iniciada_em) continue).
Now when a campaign transitions to 'rodando' for the first time, semana_iniciada_em
is set to datetime('now'). Subsequent rodando transitions leave it unchanged
(using COALESCE to preserve first-run timestamp).

Regression tests confirm:
- First transition to 'rodando' stamps semana_iniciada_em
- Subsequent transitions do not reset the timestamp
- resumeCampaign continues to work correctly (pause/resume cycle)
- All 105 tests pass

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Gustavo
2026-08-01 10:25:00 -03:00
co-authored by Claude Sonnet 5
parent fb2cd75a56
commit 577f48ab19
2 changed files with 41 additions and 3 deletions
+12 -3
View File
@@ -44,9 +44,18 @@ export function listVariants(db, campaignId) {
// status: 'rascunho' -> 'rodando' -> 'pausado' <-> 'rodando' -> 'concluido'.
// pause_reason so fica setado quando o novo status e 'pausado'.
export function setCampaignStatus(db, campaignId, status, pauseReason = null) {
db.prepare(
`UPDATE outreach_campaigns SET status = ?, pause_reason = ?, updated_at = datetime('now') WHERE id = ?`
).run(status, status === 'pausado' ? pauseReason : null, campaignId);
if (status === 'rodando') {
// When transitioning to 'rodando', stamp semana_iniciada_em only on first run (if currently NULL)
db.prepare(
`UPDATE outreach_campaigns
SET status = ?, pause_reason = ?, semana_iniciada_em = COALESCE(semana_iniciada_em, datetime('now')), updated_at = datetime('now')
WHERE id = ?`
).run(status, status === 'pausado' ? pauseReason : null, campaignId);
} else {
db.prepare(
`UPDATE outreach_campaigns SET status = ?, pause_reason = ?, updated_at = datetime('now') WHERE id = ?`
).run(status, status === 'pausado' ? pauseReason : null, campaignId);
}
return getCampaign(db, campaignId);
}
+29
View File
@@ -50,6 +50,35 @@ test('setCampaignStatus pausa e resumeCampaign retoma limpando o motivo', () =>
db.close();
});
test('setCampaignStatus rodando stamps semana_iniciada_em on first transition', () => {
const db = openDb(':memory:');
const campanha = createCampaign(db, campanhaBase());
assert.equal(campanha.semana_iniciada_em, null);
const rodando = setCampaignStatus(db, campanha.id, 'rodando');
assert.notEqual(rodando.semana_iniciada_em, null, 'semana_iniciada_em should be set on first rodando transition');
db.close();
});
test('setCampaignStatus rodando does not reset already-set semana_iniciada_em', () => {
const db = openDb(':memory:');
const campanha = createCampaign(db, campanhaBase());
// Set to rodando first time, which stamps semana_iniciada_em
setCampaignStatus(db, campanha.id, 'rodando');
// Manually set to a known past value via raw SQL
const knownTime = '2026-07-15 10:00:00';
db.prepare(`UPDATE outreach_campaigns SET semana_iniciada_em = ? WHERE id = ?`).run(knownTime, campanha.id);
// Call setCampaignStatus again while already 'rodando'
const unchanged = setCampaignStatus(db, campanha.id, 'rodando');
// Verify semana_iniciada_em was not changed
assert.equal(unchanged.semana_iniciada_em, knownTime, 'semana_iniciada_em should not be reset on subsequent rodando calls');
db.close();
});
test('resumeCampaign nao faz nada se a campanha nao estiver pausada', () => {
const db = openDb(':memory:');
const campanha = createCampaign(db, campanhaBase());