mirror of
https://github.com/we-promise/sure.git
synced 2026-09-09 08:34:26 +00:00
* fix(lunchflow): mark sync unhealthy when importer reports fetch failures (#1796) `LunchflowItem::Syncer#perform_sync` called `lunchflow_item.import_latest_lunchflow_data` but threw the result away and then ran `collect_health_stats(sync, errors: nil)`. The importer already catches per-account 429/500 fetch errors, bumps a `transactions_failed` counter, and returns `success: false` — but the syncer never inspected the return value, so the parent sync was marked completed/green even when zero transactions were imported because every fetch had been rate-limited. Capture the importer result and translate any `accounts_failed` / `transactions_failed` / `error` fields into the `{ message:, category: }` error shape `collect_health_stats` expects. The exception-path `rescue` branch is unchanged. Closes #1796 * test(lunchflow): i18n the new health messages + add syncer invariant tests (#1796) Two pieces of follow-up feedback: - @coderabbitai + @JSONbored: the three new operator-facing strings should go through I18n.t. Add keys under provider_warnings.lunchflow_* (matching the existing provider_warnings.limited_investment_data shape) and use Rails pluralization for the count-bearing entries. Other locales follow the repo's normal translation flow. - @jjmata + @JSONbored: add tests for the invariants. New LunchflowItem::SyncerTest covers: * successful import → sync healthy * accounts_failed positive → sync unhealthy with localized message * transactions_failed positive → sync unhealthy with localized message * both counters positive → both error entries recorded in order * sync raises → sync_error category + reraise (existing rescue branch) @jjmata also asked to confirm the importer contract: LunchflowItem::Importer#import returns 'success: accounts_failed == 0 && transactions_failed == 0' (see importer.rb), so the early 'return [] if import_result[:success]' guard is safe — success is never true while either counter is positive. --------- Co-authored-by: jeffrey701 <jeffrey701@users.noreply.github.com>
116 lines
3.3 KiB
Ruby
116 lines
3.3 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "test_helper"
|
|
|
|
class LunchflowItem::SyncerTest < ActiveSupport::TestCase
|
|
setup do
|
|
@lunchflow_item = lunchflow_items(:one)
|
|
@syncer = LunchflowItem::Syncer.new(@lunchflow_item)
|
|
end
|
|
|
|
test "marks sync healthy when import succeeds with no failures" do
|
|
sync = recording_sync
|
|
@lunchflow_item.expects(:import_latest_lunchflow_data).returns(
|
|
success: true,
|
|
accounts_failed: 0,
|
|
transactions_failed: 0
|
|
)
|
|
|
|
@syncer.perform_sync(sync)
|
|
|
|
assert_equal 0, sync.sync_stats["total_errors"]
|
|
assert_nil sync.sync_stats["errors"]
|
|
end
|
|
|
|
test "marks sync unhealthy when importer reports accounts_failed" do
|
|
sync = recording_sync
|
|
@lunchflow_item.expects(:import_latest_lunchflow_data).returns(
|
|
success: false,
|
|
accounts_failed: 2,
|
|
transactions_failed: 0
|
|
)
|
|
|
|
@syncer.perform_sync(sync)
|
|
|
|
assert_equal 1, sync.sync_stats["total_errors"]
|
|
assert_equal(
|
|
[ I18n.t("provider_warnings.lunchflow_accounts_failed", count: 2) ],
|
|
sync.sync_stats["errors"].map { |e| e["message"] }
|
|
)
|
|
assert_equal "lunchflow_import", sync.sync_stats["errors"].first["category"]
|
|
end
|
|
|
|
test "marks sync unhealthy when importer reports transactions_failed" do
|
|
sync = recording_sync
|
|
@lunchflow_item.expects(:import_latest_lunchflow_data).returns(
|
|
success: false,
|
|
accounts_failed: 0,
|
|
transactions_failed: 3
|
|
)
|
|
|
|
@syncer.perform_sync(sync)
|
|
|
|
assert_equal 1, sync.sync_stats["total_errors"]
|
|
assert_equal(
|
|
[ I18n.t("provider_warnings.lunchflow_transactions_failed", count: 3) ],
|
|
sync.sync_stats["errors"].map { |e| e["message"] }
|
|
)
|
|
end
|
|
|
|
test "records both failure categories when accounts and transactions both fail" do
|
|
sync = recording_sync
|
|
@lunchflow_item.expects(:import_latest_lunchflow_data).returns(
|
|
success: false,
|
|
accounts_failed: 1,
|
|
transactions_failed: 4
|
|
)
|
|
|
|
@syncer.perform_sync(sync)
|
|
|
|
assert_equal 2, sync.sync_stats["total_errors"]
|
|
assert_equal(
|
|
[
|
|
I18n.t("provider_warnings.lunchflow_accounts_failed", count: 1),
|
|
I18n.t("provider_warnings.lunchflow_transactions_failed", count: 4)
|
|
],
|
|
sync.sync_stats["errors"].map { |e| e["message"] }
|
|
)
|
|
end
|
|
|
|
test "captures sync_error category and reraises when import raises" do
|
|
sync = recording_sync
|
|
@lunchflow_item.expects(:import_latest_lunchflow_data).raises(StandardError, "boom")
|
|
|
|
assert_raises(StandardError) do
|
|
@syncer.perform_sync(sync)
|
|
end
|
|
|
|
assert_equal 1, sync.sync_stats["total_errors"]
|
|
error = sync.sync_stats["errors"].first
|
|
assert_equal "boom", error["message"]
|
|
assert_equal "sync_error", error["category"]
|
|
end
|
|
|
|
private
|
|
|
|
def recording_sync
|
|
Class.new do
|
|
attr_accessor :sync_stats, :status_text
|
|
attr_reader :updates, :window_start_date, :window_end_date
|
|
|
|
def initialize
|
|
@sync_stats = {}
|
|
@updates = []
|
|
@window_start_date = nil
|
|
@window_end_date = nil
|
|
end
|
|
|
|
def update!(attributes)
|
|
@updates << attributes
|
|
self.sync_stats = attributes[:sync_stats] if attributes.key?(:sync_stats)
|
|
self.status_text = attributes[:status_text] if attributes.key?(:status_text)
|
|
end
|
|
end.new
|
|
end
|
|
end
|