diff --git a/app/services/heartbeat_import_service.rb b/app/services/heartbeat_import_service.rb index f6ee233d8..1fe0f374f 100644 --- a/app/services/heartbeat_import_service.rb +++ b/app/services/heartbeat_import_service.rb @@ -11,7 +11,7 @@ def self.import_from_file(file_content, user, on_progress: nil, progress_interva flush = lambda do next if heartbeat_batch.empty? result = HeartbeatIngest.call(user:, mode: :import, heartbeats: heartbeat_batch, - user_agents_by_id:, schedule_rollup_refresh: false) + user_agents_by_id:) imported_count += result.persisted_count errors.concat(result.errors) heartbeat_batch.clear @@ -30,7 +30,6 @@ def self.import_from_file(file_content, user, on_progress: nil, progress_interva raise StandardError, "Expected a heartbeat export JSON file." if total_count.zero? flush.call - HeartbeatIngest.schedule_rollup_refresh(user:) if imported_count.positive? elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time { success: true, imported_count:, total_count:, diff --git a/test/services/heartbeat_import_service_test.rb b/test/services/heartbeat_import_service_test.rb index f5b84e706..c071e2338 100644 --- a/test/services/heartbeat_import_service_test.rb +++ b/test/services/heartbeat_import_service_test.rb @@ -1,6 +1,33 @@ require "test_helper" class HeartbeatImportServiceTest < ActiveSupport::TestCase + include ActiveJob::TestHelper + + test "a malformed dump after a committed batch still invalidates rollups" do + original_cache = Rails.cache + original_adapter = ActiveJob::Base.queue_adapter + ActiveJob::Base.queue_adapter = :test + Rails.cache = ActiveSupport::Cache::MemoryStore.new + user = create(:user) + create(:heartbeat, user: user, time: 1_800_000_000.0) + DashboardRollupRefreshService.new(user: user).call + clear_enqueued_jobs + Rails.cache.clear + row = { entity: "old.rb", time: 1_700_000_000.0, type: "file" }.to_json + truncated_dump = '{"heartbeats":[' + ([ row ] * HeartbeatImportService::BATCH_SIZE).join(",") + ',{"entity":' + + result = HeartbeatImportService.import_from_file(StringIO.new(truncated_dump), user) + + assert_not result[:success] + assert_equal 1, result[:imported_count] + assert_equal 2, user.heartbeats.count + assert DashboardRollup.dirty?(user.id) + assert_enqueued_with(job: DashboardRollupRefreshJob, args: [ user.id ]) + ensure + Rails.cache = original_cache + ActiveJob::Base.queue_adapter = original_adapter + end + test "sanitizes null bytes without losing valid batch rows and deduplicates replays" do user = create(:user) rows = [