test(automations): fix awaiting-agent key format and tighten job assertions

- Match the microsecond-integer (strftime %s%6N) awaiting-agent key format,
  fixing the CI failure from float epoch imprecision.
- Assert exactly one ProcessPendingExecutionJob is enqueued and the future row
  is not, so an early-fire regression is caught.
- Count the reply-chase follow-up as exactly one so a double-send regression fails.
- Stub the ActionService instance's perform (not new) to raise, exercising the
  job's error/retry path rather than only constructor failures.
This commit is contained in:
Tanmay Deep Sharma
2026-07-15 14:42:59 +05:30
parent d7b25d7db0
commit 40e529fe4d
3 changed files with 10 additions and 6 deletions
@@ -103,7 +103,9 @@ RSpec.describe AutomationRules::ProcessPendingExecutionJob do
end
it 'leaves the row processing and reports the error when an action blows up' do
allow(AutomationRules::ActionService).to receive(:new).and_raise(StandardError, 'boom')
action_service = instance_double(AutomationRules::ActionService)
allow(AutomationRules::ActionService).to receive(:new).and_return(action_service)
allow(action_service).to receive(:perform).and_raise(StandardError, 'boom')
allow(ChatwootExceptionTracker).to receive(:new).and_call_original
job.perform(pending_execution.reload)
@@ -124,7 +126,7 @@ RSpec.describe AutomationRules::ProcessPendingExecutionJob do
job.perform(row.reload)
expect(row.reload).to be_executed
expect(conversation.messages.outgoing.pluck(:content)).to include('Just checking in')
expect(conversation.messages.outgoing.where(content: 'Just checking in').count).to eq(1)
end
it 'cancels the follow-up when the customer replied before it was due' do
@@ -10,9 +10,11 @@ RSpec.describe AutomationRules::TriggerPendingExecutionsJob do
it 'enqueues a per-row job for due pending rows but not future ones' do
due_row = create(:automation_rule_pending_execution, account: account, conversation: conversation, due_at: 1.minute.ago)
create(:automation_rule_pending_execution, account: account, due_at: 1.hour.from_now)
future_row = create(:automation_rule_pending_execution, account: account, due_at: 1.hour.from_now)
expect { job.perform }.to have_enqueued_job(AutomationRules::ProcessPendingExecutionJob).exactly(:once).with(due_row)
expect { job.perform }.to have_enqueued_job(AutomationRules::ProcessPendingExecutionJob).exactly(:once)
expect(AutomationRules::ProcessPendingExecutionJob).to have_been_enqueued.with(due_row)
expect(AutomationRules::ProcessPendingExecutionJob).not_to have_been_enqueued.with(future_row)
end
it 're-enqueues stale processing rows so they get retried' do
@@ -20,7 +20,7 @@ RSpec.describe AutomationRulePendingExecution do
it 'derives awaiting_agent episodes from waiting_since (sub-second) for incoming messages' do
message = create(:message, conversation: conversation, account: account, message_type: :incoming)
expect(described_class.episode_key_for(conversation.reload, message)).to eq("awaiting_agent:#{conversation.waiting_since.to_f}")
expect(described_class.episode_key_for(conversation.reload, message)).to eq("awaiting_agent:#{conversation.waiting_since.strftime('%s%6N')}")
end
it 'distinguishes two waiting periods that fall within the same second' do
@@ -40,7 +40,7 @@ RSpec.describe AutomationRulePendingExecution do
# Once waiting_since settles to the message's created_at, the strict fire-time key matches.
conversation.update!(waiting_since: message.created_at)
expect(armed_key).to eq("awaiting_agent:#{message.created_at.to_f}")
expect(armed_key).to eq("awaiting_agent:#{message.created_at.strftime('%s%6N')}")
expect(armed_key).to eq(described_class.episode_key_for(conversation.reload, message))
end