discourse-ai/spec/jobs/regular/localize_categories_spec.rb
Natalie Tay 3e87e92631
DEV: Remove 'experimental' from translation features (#1439)
* DEV: Remove 'experimental' from translation features

* include compat

* include compat
2025-06-19 12:23:56 +08:00

191 lines
5.5 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
SiteSetting.discourse_ai_enabled = true
Fabricate(:fake_model).tap do |fake_llm|
SiteSetting.public_send("ai_translation_model=", "custom:#{fake_llm.id}")
end
SiteSetting.ai_translation_enabled = true
SiteSetting.content_localization_supported_locales = "pt|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({})
end
it "does nothing when ai_translation_enabled is disabled" do
SiteSetting.ai_translation_enabled = false
DiscourseAi::Translation::CategoryLocalizer.expects(:localize).never
job.execute({})
end
it "does nothing when no target languages are configured" do
SiteSetting.content_localization_supported_locales = ""
DiscourseAi::Translation::CategoryLocalizer.expects(:localize).never
job.execute({})
end
it "does nothing when no categories exist" do
Category.destroy_all
DiscourseAi::Translation::CategoryLocalizer.expects(:localize).never
job.execute({})
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")
.times(number_of_categories)
DiscourseAi::Translation::CategoryLocalizer
.expects(:localize)
.with(is_a(Category), "zh_CN")
.times(number_of_categories)
job.execute({})
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").never
DiscourseAi::Translation::CategoryLocalizer
.expects(:localize)
.with(is_a(Category), "zh_CN")
.never
job.execute({})
end
it "continues from a specified category ID" do
category1 = Fabricate(:category, name: "First", description: "First description", locale: "en")
category2 =
Fabricate(:category, name: "Second", description: "Second description", locale: "en")
DiscourseAi::Translation::CategoryLocalizer
.expects(:localize)
.with(category1, any_parameters)
.never
DiscourseAi::Translation::CategoryLocalizer
.expects(:localize)
.with(category2, any_parameters)
.twice
job.execute(from_category_id: category2.id)
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")
.raises(StandardError.new("API error"))
DiscourseAi::Translation::CategoryLocalizer.expects(:localize).with(category1, "zh_CN").once
expect { job.execute({}) }.not_to raise_error
end
it "enqueues the next batch when there are more categories" do
Category.update_all(locale: "en")
Jobs.run_later!
freeze_time
Jobs::LocalizeCategories.const_set(:BATCH_SIZE, 1)
job.execute({})
Category.all.each do |category|
puts category.id
expect_job_enqueued(
job: :localize_categories,
args: {
from_category_id: category.id + 1,
},
at: 10.seconds.from_now,
)
end
Jobs::LocalizeCategories.send(:remove_const, :BATCH_SIZE)
Jobs::LocalizeCategories.const_set(:BATCH_SIZE, 50)
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({})
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({})
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({}) }.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({})
end
end