fix(captain): compare date custom attributes stored as strings in audience matcher

Custom date attributes persist as ISO strings in jsonb, so is_greater_than/
is_less_than fell into the BigDecimal branch and never matched. Parse ISO
date strings as times, mirroring Contacts::FilterService coercion.
This commit is contained in:
Pranav
2026-07-14 17:45:12 -07:00
parent bc79897419
commit cba79636cb
2 changed files with 17 additions and 0 deletions
@@ -100,6 +100,8 @@ class Captain::AudienceMatcher
def compare(actual, expected)
return nil if actual.blank?
actual = Time.zone.parse(actual) if iso_date_string?(actual)
if actual.is_a?(Date) || actual.acts_like?(:time)
actual.to_time <=> Time.zone.parse(expected.to_s)
else
@@ -109,6 +111,14 @@ class Captain::AudienceMatcher
nil
end
# Custom date attributes store ISO strings in jsonb; treat them as dates the way
# Contacts::FilterService does (it casts them in SQL).
def iso_date_string?(value)
value.is_a?(String) && Date.iso8601(value).present?
rescue ArgumentError
false
end
def older_than_days?(actual, days)
date = to_date(actual)
date.present? && date < Time.zone.today - days.to_i.days
@@ -61,6 +61,13 @@ RSpec.describe Captain::AudienceMatcher do
expect(matches?(leaf('created_at', 'days_before', '30'))).to be(true)
expect(matches?(leaf('created_at', 'days_before', '60'))).to be(false)
end
it 'compares date custom attributes stored as ISO strings' do
contact.update!(custom_attributes: contact.custom_attributes.merge('signed_up_on' => '2024-01-15'))
expect(matches?(leaf('signed_up_on', 'is_greater_than', '2024-01-01'))).to be(true)
expect(matches?(leaf('signed_up_on', 'is_less_than', '2024-01-01'))).to be(false)
expect(matches?(leaf('signed_up_on', 'is_less_than', '2024-02-01'))).to be(true)
end
end
context 'with labels' do