From f8a566604289b989bff4182b0cc01283ce888f22 Mon Sep 17 00:00:00 2001 From: Bernardo Anderson Date: Mon, 16 Feb 2026 17:28:58 -0600 Subject: [PATCH] Change account webhook creation from after_create to after_commit This ensures that CareerPlug webhooks are only created after the account transaction has been successfully committed to the database. This prevents orphaned webhook records if account creation fails or is rolled back. The change improves data consistency and follows Rails best practices for callbacks that create associated records or have external side effects. Includes tests to verify that: - Webhooks are created after successful account creation - Webhooks are not created if account creation fails/rolls back - Webhooks are not created when CAREERPLUG_WEBHOOK_SECRET is blank --- app/models/account.rb | 2 +- .../account_create_careerplug_webhook_spec.rb | 65 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 spec/models/account_create_careerplug_webhook_spec.rb diff --git a/app/models/account.rb b/app/models/account.rb index 6a9d2a44..e9acfa12 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -57,7 +57,7 @@ class Account < ApplicationRecord validates :external_account_id, uniqueness: true, allow_nil: true - after_create :create_careerplug_webhook + after_commit :create_careerplug_webhook, on: :create scope :active, -> { where(archived_at: nil) } diff --git a/spec/models/account_create_careerplug_webhook_spec.rb b/spec/models/account_create_careerplug_webhook_spec.rb new file mode 100644 index 00000000..140a9792 --- /dev/null +++ b/spec/models/account_create_careerplug_webhook_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Account, '#create_careerplug_webhook' do + around do |example| + original_secret = ENV.fetch('CAREERPLUG_WEBHOOK_SECRET', nil) + original_url = ENV.fetch('CAREERPLUG_WEBHOOK_URL', nil) + + # Set required env vars for webhook creation + ENV['CAREERPLUG_WEBHOOK_SECRET'] = 'test_secret' + ENV['CAREERPLUG_WEBHOOK_URL'] = 'http://example.com/webhook' + + example.run + + # Restore original env vars + ENV['CAREERPLUG_WEBHOOK_SECRET'] = original_secret + ENV['CAREERPLUG_WEBHOOK_URL'] = original_url + end + + describe 'CareerPlug webhook creation' do + it 'creates webhook after successful account creation' do + account = build(:account) + expect(account.webhook_urls).to be_empty + + account.save! + + expect(account.webhook_urls.count).to eq(1) + webhook = account.webhook_urls.first + expect(webhook.url).to eq('http://example.com/webhook') + expect(webhook.events).to eq(['form.viewed', 'form.started', 'form.completed', 'form.declined']) + expect(webhook.secret).to eq({ 'X-CareerPlug-Secret' => 'test_secret' }) + end + + it 'does not create webhook if account creation fails' do + # This test verifies that after_commit behavior works correctly + # by simulating a transaction rollback + + expect do + described_class.transaction do + create(:account) + # Simulate some error that would cause rollback + raise ActiveRecord::Rollback + end + end.not_to change(described_class, :count) + + expect do + described_class.transaction do + create(:account) + raise ActiveRecord::Rollback + end + end.not_to change(WebhookUrl, :count) + end + + it 'does not create webhook when CAREERPLUG_WEBHOOK_SECRET is blank' do + original_secret = ENV.fetch('CAREERPLUG_WEBHOOK_SECRET', nil) + ENV['CAREERPLUG_WEBHOOK_SECRET'] = '' + + account = create(:account) + expect(account.webhook_urls.count).to eq(0) + + ENV['CAREERPLUG_WEBHOOK_SECRET'] = original_secret + end + end +end