mirror of
https://github.com/System-End/hackatime.git
synced 2026-04-19 16:38:23 +00:00
187 lines
5.5 KiB
Ruby
187 lines
5.5 KiB
Ruby
require "test_helper"
|
|
require "webmock/minitest"
|
|
|
|
class HeartbeatImportSourceSyncJobTest < ActiveJob::TestCase
|
|
setup do
|
|
Flipper.enable(:wakatime_imports_mirrors)
|
|
end
|
|
|
|
teardown do
|
|
Flipper.disable(:wakatime_imports_mirrors)
|
|
end
|
|
|
|
def create_source(user:, **attrs)
|
|
user.create_heartbeat_import_source!(
|
|
{
|
|
provider: :wakatime_compatible,
|
|
endpoint_url: "https://wakatime.com/api/v1",
|
|
encrypted_api_key: "import-key",
|
|
sync_enabled: true,
|
|
status: :idle
|
|
}.merge(attrs)
|
|
)
|
|
end
|
|
|
|
def queued_jobs_for(job_class)
|
|
GoodJob::Job.where(job_class: job_class)
|
|
end
|
|
|
|
test "full-history default schedules backfill windows and re-enqueues coordinator" do
|
|
GoodJob::Job.delete_all
|
|
user = User.create!(timezone: "UTC")
|
|
source = create_source(user: user)
|
|
|
|
stub_request(:get, "https://wakatime.com/api/v1/users/current/all_time_since_today")
|
|
.to_return(
|
|
status: 200,
|
|
body: {
|
|
data: {
|
|
range: {
|
|
start_date: (Date.current - 10.days).iso8601
|
|
}
|
|
}
|
|
}.to_json,
|
|
headers: { "Content-Type" => "application/json" }
|
|
)
|
|
|
|
HeartbeatImportSourceSyncJob.perform_now(source.id)
|
|
|
|
day_jobs = queued_jobs_for("HeartbeatImportSourceSyncDayJob")
|
|
sync_jobs = queued_jobs_for("HeartbeatImportSourceSyncJob")
|
|
|
|
assert_equal 5, day_jobs.count
|
|
assert_equal 1, sync_jobs.count
|
|
|
|
source.reload
|
|
assert source.backfilling?
|
|
assert_equal(Date.current - 5.days, source.backfill_cursor_date)
|
|
end
|
|
|
|
test "range override limits scheduled days" do
|
|
GoodJob::Job.delete_all
|
|
user = User.create!(timezone: "UTC")
|
|
start_date = Date.current - 2.days
|
|
end_date = Date.current - 1.day
|
|
source = create_source(
|
|
user: user,
|
|
initial_backfill_start_date: start_date,
|
|
initial_backfill_end_date: end_date
|
|
)
|
|
|
|
HeartbeatImportSourceSyncJob.perform_now(source.id)
|
|
|
|
day_jobs = queued_jobs_for("HeartbeatImportSourceSyncDayJob")
|
|
day_args = day_jobs.map { |job| job.serialized_params.fetch("arguments").last }
|
|
|
|
assert_equal 2, day_jobs.count
|
|
assert_includes day_args, start_date.iso8601
|
|
assert_includes day_args, end_date.iso8601
|
|
end
|
|
|
|
test "ongoing sync enqueues today and yesterday" do
|
|
GoodJob::Job.delete_all
|
|
user = User.create!(timezone: "UTC")
|
|
source = create_source(
|
|
user: user,
|
|
status: :syncing,
|
|
initial_backfill_start_date: Date.current - 7.days,
|
|
initial_backfill_end_date: Date.current,
|
|
backfill_cursor_date: nil,
|
|
last_synced_at: Time.current
|
|
)
|
|
|
|
HeartbeatImportSourceSyncJob.perform_now(source.id)
|
|
|
|
day_jobs = queued_jobs_for("HeartbeatImportSourceSyncDayJob")
|
|
scheduled_dates = day_jobs.map { |job| Date.iso8601(job.serialized_params.fetch("arguments").last) }
|
|
|
|
assert_equal 2, day_jobs.count
|
|
assert_includes scheduled_dates, Date.current
|
|
assert_includes scheduled_dates, Date.yesterday
|
|
end
|
|
|
|
test "day job imports and dedupes by fields_hash" do
|
|
user = User.create!(timezone: "UTC")
|
|
source = create_source(user: user, status: :syncing)
|
|
timestamp = Time.current.to_f
|
|
|
|
payload = [
|
|
{
|
|
entity: "src/a.rb",
|
|
type: "file",
|
|
category: "coding",
|
|
project: "alpha",
|
|
language: "Ruby",
|
|
editor: "VS Code",
|
|
time: timestamp
|
|
},
|
|
{
|
|
entity: "src/a.rb",
|
|
type: "file",
|
|
category: "coding",
|
|
project: "alpha",
|
|
language: "Ruby",
|
|
editor: "VS Code",
|
|
time: timestamp
|
|
}
|
|
]
|
|
|
|
stub_request(:get, "https://wakatime.com/api/v1/users/current/heartbeats")
|
|
.with(query: { "date" => Date.current.iso8601 })
|
|
.to_return(
|
|
status: 200,
|
|
body: { data: payload }.to_json,
|
|
headers: { "Content-Type" => "application/json" }
|
|
)
|
|
|
|
HeartbeatImportSourceSyncDayJob.perform_now(source.id, Date.current.iso8601)
|
|
|
|
assert_equal 1, user.heartbeats.where(source_type: :wakapi_import).count
|
|
assert source.reload.last_synced_at.present?
|
|
end
|
|
|
|
test "day job pauses source on auth errors" do
|
|
user = User.create!(timezone: "UTC")
|
|
source = create_source(user: user, status: :syncing)
|
|
|
|
stub_request(:get, "https://wakatime.com/api/v1/users/current/heartbeats")
|
|
.with(query: { "date" => Date.current.iso8601 })
|
|
.to_return(status: 401, body: "{}")
|
|
|
|
HeartbeatImportSourceSyncDayJob.perform_now(source.id, Date.current.iso8601)
|
|
|
|
source.reload
|
|
assert source.paused?
|
|
assert_not source.sync_enabled
|
|
assert_includes source.last_error_message, "Authentication failed"
|
|
end
|
|
|
|
test "day job marks transient errors for retry" do
|
|
user = User.create!(timezone: "UTC")
|
|
source = create_source(user: user, status: :syncing)
|
|
|
|
stub_request(:get, "https://wakatime.com/api/v1/users/current/heartbeats")
|
|
.with(query: { "date" => Date.current.iso8601 })
|
|
.to_return(status: 500, body: "{}")
|
|
|
|
assert_raises(WakatimeCompatibleClient::TransientError) do
|
|
HeartbeatImportSourceSyncDayJob.new.perform(source.id, Date.current.iso8601)
|
|
end
|
|
|
|
source.reload
|
|
assert source.failed?
|
|
assert_equal 1, source.consecutive_failures
|
|
end
|
|
|
|
test "coordinator does nothing when imports and mirrors are disabled" do
|
|
GoodJob::Job.delete_all
|
|
user = User.create!(timezone: "UTC")
|
|
source = create_source(user: user)
|
|
Flipper.disable(:wakatime_imports_mirrors)
|
|
|
|
HeartbeatImportSourceSyncJob.perform_now(source.id)
|
|
|
|
assert_equal 0, queued_jobs_for("HeartbeatImportSourceSyncDayJob").count
|
|
assert_equal "idle", source.reload.status
|
|
end
|
|
end
|