discourse-ai/spec/jobs/regular/localize_categories_spec.rb
2025-07-16 10:56:18 -07:00

173 lines
5.1 KiB
Ruby

# frozen_string_literal: true
describe Jobs::LocalizeCategories do
subject(:job) { described_class.new }
def localize_all_categories(*locales)
Category.all.each do |category|
locales.each { |locale| Fabricate(:category_localization, category:, locale:, name: "x") }
end
end
before do
assign_fake_provider_to(:ai_default_llm_model)
SiteSetting.discourse_ai_enabled = true
SiteSetting.ai_translation_enabled = true
SiteSetting.content_localization_supported_locales = "pt_BR|zh_CN"
Jobs.run_immediately!
end
it "does nothing when DiscourseAi::Translation::CategoryLocalizer is disabled" do
SiteSetting.discourse_ai_enabled = false
DiscourseAi::Translation::CategoryLocalizer.expects(:localize).never
job.execute({ limit: 10 })
end
it "does nothing when ai_translation_enabled is disabled" do
SiteSetting.ai_translation_enabled = false
DiscourseAi::Translation::CategoryLocalizer.expects(:localize).never
job.execute({ limit: 10 })
end
it "does nothing when no target languages are configured" do
SiteSetting.content_localization_supported_locales = ""
DiscourseAi::Translation::CategoryLocalizer.expects(:localize).never
job.execute({ limit: 10 })
end
it "does nothing when no categories exist" do
Category.destroy_all
DiscourseAi::Translation::CategoryLocalizer.expects(:localize).never
job.execute({ limit: 10 })
end
it "does nothing when the limit is zero" do
DiscourseAi::Translation::CategoryLocalizer.expects(:localize).never
expect { job.execute({}) }.to raise_error(Discourse::InvalidParameters, /limit/)
job.execute({ limit: 0 })
end
it "translates categories to the configured locales" do
Category.update_all(locale: "en")
number_of_categories = Category.count
DiscourseAi::Translation::CategoryLocalizer
.expects(:localize)
.with(is_a(Category), "pt_BR")
.times(number_of_categories)
DiscourseAi::Translation::CategoryLocalizer
.expects(:localize)
.with(is_a(Category), "zh_CN")
.times(number_of_categories)
job.execute({ limit: 10 })
end
it "limits the number of localizations" do
SiteSetting.content_localization_supported_locales = "pt"
6.times { Fabricate(:category) }
Category.update_all(locale: "en")
DiscourseAi::Translation::CategoryLocalizer
.expects(:localize)
.with(is_a(Category), "pt")
.times(5)
job.execute({ limit: 5 })
end
it "skips categories that already have localizations" do
localize_all_categories("pt", "zh_CN")
DiscourseAi::Translation::CategoryLocalizer
.expects(:localize)
.with(is_a(Category), "pt_BR")
.never
DiscourseAi::Translation::CategoryLocalizer
.expects(:localize)
.with(is_a(Category), "zh_CN")
.never
job.execute({ limit: 10 })
end
it "handles translation errors gracefully" do
localize_all_categories("pt", "zh_CN")
category1 = Fabricate(:category, name: "First", description: "First description", locale: "en")
DiscourseAi::Translation::CategoryLocalizer
.expects(:localize)
.with(category1, "pt_BR")
.once
.raises(StandardError.new("API error"))
DiscourseAi::Translation::CategoryLocalizer.expects(:localize).with(category1, "zh_CN").once
expect { job.execute({ limit: 10 }) }.not_to raise_error
end
it "skips read-restricted categories when configured" do
SiteSetting.ai_translation_backfill_limit_to_public_content = true
category1 = Fabricate(:category, name: "Public Category", read_restricted: false, locale: "en")
category2 = Fabricate(:category, name: "Private Category", read_restricted: true, locale: "en")
DiscourseAi::Translation::CategoryLocalizer
.expects(:localize)
.with(category1, any_parameters)
.twice
DiscourseAi::Translation::CategoryLocalizer
.expects(:localize)
.with(category2, any_parameters)
.never
job.execute({ limit: 10 })
end
it "skips creating localizations in the same language as the category's locale" do
Category.update_all(locale: "pt")
DiscourseAi::Translation::CategoryLocalizer.expects(:localize).with(is_a(Category), "pt").never
DiscourseAi::Translation::CategoryLocalizer
.expects(:localize)
.with(is_a(Category), "zh_CN")
.times(Category.count)
job.execute({ limit: 10 })
end
it "deletes existing localizations that match the category's locale" do
# update all categories to portuguese
Category.update_all(locale: "pt")
localize_all_categories("pt", "zh_CN")
expect { job.execute({ limit: 10 }) }.to change {
CategoryLocalization.exists?(locale: "pt")
}.from(true).to(false)
end
it "doesn't process categories with nil locale" do
# Add a category with nil locale
nil_locale_category = Fabricate(:category, name: "No Locale", locale: nil)
# Make sure our query for categories with non-null locales excludes it
DiscourseAi::Translation::CategoryLocalizer
.expects(:localize)
.with(nil_locale_category, any_parameters)
.never
job.execute({ limit: 10 })
end
end