hackatime/test/services/heartbeat_import_runner_test.rb
Mahad Kalam 28fa174861
Add Sentry monitoring for previously unreported errors (#1066)
* Add Sentry monitoring for previously unreported errors

* Fix

* Fixes

* whoops!
2026-03-13 11:06:12 +00:00

195 lines
6.1 KiB
Ruby

require "test_helper"
class HeartbeatImportRunnerTest < ActiveSupport::TestCase
include ActiveJob::TestHelper
setup do
ActionMailer::Base.deliveries.clear
@original_queue_adapter = ActiveJob::Base.queue_adapter
ActiveJob::Base.queue_adapter = :test
end
teardown do
ActionMailer::Base.deliveries.clear
clear_enqueued_jobs
ActiveJob::Base.queue_adapter = @original_queue_adapter
end
test "run_import emails the user when a wakatime import succeeds" do
user = User.create!(timezone: "UTC")
user.email_addresses.create!(email: "success-#{SecureRandom.hex(4)}@example.com", source: :signing_in)
run = user.heartbeat_import_runs.create!(
source_kind: :wakatime_dump,
state: :queued,
encrypted_api_key: "secret"
)
file = Tempfile.new([ "heartbeats", ".json" ])
file.write(
{
heartbeats: [
{
entity: "/tmp/test.rb",
type: "file",
time: 1_700_000_000.0,
project: "hackatime",
language: "Ruby",
is_write: true
}
]
}.to_json
)
file.close
HeartbeatImportRunner.run_import(import_run_id: run.id, file_path: file.path)
run.reload
assert_equal "completed", run.state
assert_nil run.encrypted_api_key
assert_enqueued_jobs 1, only: ActionMailer::MailDeliveryJob
ensure
file&.unlink
end
test "run_import does not overwrite a completed run if completion follow-up fails" do
user = User.create!(timezone: "UTC")
user.email_addresses.create!(email: "post-complete-#{SecureRandom.hex(4)}@example.com", source: :signing_in)
run = user.heartbeat_import_runs.create!(
source_kind: :wakatime_dump,
state: :queued,
encrypted_api_key: "secret"
)
file = Tempfile.new([ "heartbeats", ".json" ])
file.write(
{
heartbeats: [
{
entity: "/tmp/test.rb",
type: "file",
time: 1_700_000_000.0,
project: "hackatime",
language: "Ruby",
is_write: true
}
]
}.to_json
)
file.close
singleton_class = HeartbeatImportRunner.singleton_class
singleton_class.alias_method :__original_send_completion_email_for_test, :send_completion_email
singleton_class.define_method(:send_completion_email) do |_completed_run|
raise "mail transport failed"
end
begin
HeartbeatImportRunner.run_import(import_run_id: run.id, file_path: file.path)
ensure
singleton_class.alias_method :send_completion_email, :__original_send_completion_email_for_test
singleton_class.remove_method :__original_send_completion_email_for_test
file&.unlink
end
run.reload
assert_equal "completed", run.state
assert_nil run.error_message
assert_nil run.encrypted_api_key
assert_empty ActionMailer::Base.deliveries
end
test "fail_run_for_error! emails the user about invalid api keys" do
user = User.create!(timezone: "UTC")
user.email_addresses.create!(email: "invalid-key-#{SecureRandom.hex(4)}@example.com", source: :signing_in)
run = user.heartbeat_import_runs.create!(
source_kind: :wakatime_dump,
state: :queued,
encrypted_api_key: "secret"
)
HeartbeatImportRunner.fail_run_for_error!(
import_run_id: run.id,
error: HeartbeatImportDumpClient::AuthenticationError.new("Authentication failed (401)", status: 401)
)
run.reload
assert_equal "failed", run.state
assert_equal "WakaTime rejected the import because the API key is invalid.", run.error_message
assert_nil run.encrypted_api_key
assert_enqueued_jobs 1, only: ActionMailer::MailDeliveryJob
end
test "fail_run_for_error! includes slack guidance for hackatime v1 provider errors" do
user = User.create!(timezone: "UTC")
user.email_addresses.create!(email: "provider-error-#{SecureRandom.hex(4)}@example.com", source: :signing_in)
run = user.heartbeat_import_runs.create!(
source_kind: :hackatime_v1_dump,
state: :queued,
encrypted_api_key: "secret"
)
HeartbeatImportRunner.fail_run_for_error!(
import_run_id: run.id,
error: HeartbeatImportDumpClient::TransientError.new("Request failed with status 500", status: 500)
)
run.reload
assert_equal "failed", run.state
assert_equal "Hackatime v1 ran into an error while processing the import. Please reach out to #hackatime-help on Slack.", run.error_message
assert_nil run.encrypted_api_key
assert_enqueued_jobs 1, only: ActionMailer::MailDeliveryJob
end
test "start_remote_import bypasses cooldown for superadmins" do
user = User.create!(timezone: "UTC", admin_level: :superadmin)
Flipper.enable_actor(:imports, user)
user.heartbeat_import_runs.create!(
source_kind: :wakatime_dump,
state: :completed,
encrypted_api_key: "old-secret",
remote_requested_at: 1.minute.ago
)
assert_difference -> { user.heartbeat_import_runs.count }, +1 do
run = HeartbeatImportRunner.start_remote_import(
user: user,
provider: "wakatime_dump",
api_key: "new-secret"
)
assert_equal "wakatime_dump", run.source_kind
assert_equal "queued", run.state
end
end
test "serialize omits cooldown for superadmins" do
user = User.create!(timezone: "UTC", admin_level: :superadmin)
run = user.heartbeat_import_runs.create!(
source_kind: :wakatime_dump,
state: :completed,
encrypted_api_key: "secret",
remote_requested_at: 1.minute.ago
)
payload = HeartbeatImportRunner.serialize(run)
assert_nil payload[:cooldown_until]
end
test "refreshable_remote_run? stops once a remote import is downloading or importing" do
%i[downloading_dump importing].each do |state|
user = User.create!(timezone: "UTC")
Flipper.enable_actor(:imports, user)
run = user.heartbeat_import_runs.create!(
source_kind: :wakatime_dump,
state: state,
encrypted_api_key: "secret"
)
run.update_column(:updated_at, 10.seconds.ago)
assert_not HeartbeatImportRunner.refreshable_remote_run?(run)
end
end
end