0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-09 21:45:25 +08:00
discourse/spec/services/custom_emoji/import_row_spec.rb
Martin Brennan b736e72b1c
DEV: Retroactive review and refactor of custom emoji bulk import/export (#41860)
Followup bd3906ce64

Converts the new endpoints made in the original PR into
service classes, using AI to drive implementation based
on the service authoring skill:

* CustomEmoji::Export
* CustomEmoji::PreviewImport
* CustomEmoji::ConfirmImport

In addition, some supporting action, lib, and serializer
classes have been added. This fixed a couple of
bugs as well:

* Import names now go through `Emoji.sanitize_emoji_name` like
single-emoji create
* No-group imports no longer persist a literal "default" string
* Two 500s (suspicious zip entries, malformed CSV) became proper 422s
* Duplicate CSV names are flagged at preview instead of exploding the
confirm transaction
*` Emoji.clear_cache `moved outside the transaction
2026-07-22 10:06:54 +10:00

89 lines
3 KiB
Ruby
Vendored

# frozen_string_literal: true
RSpec.describe CustomEmoji::ImportRow do
describe "#stage" do
subject(:staged_row) { row.stage(upload:, existing_emoji:) }
fab!(:upload)
let(:row) { described_class.new(index: 0, name: "party", group: "fun", filename: "party.png") }
context "when no emoji exists with that name" do
let(:existing_emoji) { nil }
it "categorizes the row as new and records the incoming upload" do
expect(staged_row.category).to eq(described_class::CATEGORY_NEW)
expect(staged_row.upload_id).to eq(upload.id)
expect(staged_row.incoming_url).to eq(upload.url)
end
end
context "when an emoji exists with the same image and group" do
fab!(:existing_emoji) { Fabricate(:custom_emoji, name: "party", group: "fun") }
let(:upload) { existing_emoji.upload }
it "categorizes the row as identical" do
expect(staged_row.category).to eq(described_class::CATEGORY_IDENTICAL)
end
end
context "when an emoji exists with the same image but another group" do
fab!(:existing_emoji) { Fabricate(:custom_emoji, name: "party", group: "old") }
let(:upload) { existing_emoji.upload }
it "categorizes the row as a group conflict and records the existing state" do
expect(staged_row.category).to eq(described_class::CATEGORY_CONFLICT_GROUP)
expect(staged_row.existing_group).to eq("old")
expect(staged_row.existing_url).to eq(existing_emoji.upload.url)
end
end
context "when an emoji exists with another image but the same group" do
fab!(:existing_emoji) { Fabricate(:custom_emoji, name: "party", group: "fun") }
it "categorizes the row as an image conflict" do
expect(staged_row.category).to eq(described_class::CATEGORY_CONFLICT_IMAGE)
end
end
context "when an emoji exists with another image and another group" do
fab!(:existing_emoji) { Fabricate(:custom_emoji, name: "party", group: "old") }
it "categorizes the row as a conflict on both" do
expect(staged_row.category).to eq(described_class::CATEGORY_CONFLICT_BOTH)
end
end
end
describe "#mark_invalid" do
it "accumulates errors and categorizes the row as invalid" do
row = described_class.new(index: 0, name: "party", group: nil, filename: "party.png")
row.mark_invalid("first error", "second error")
expect(row).to be_invalid
expect(row.errors).to eq(["first error", "second error"])
end
end
describe ".from_h" do
it "round-trips a staged row through its hash representation" do
row =
described_class.new(
index: 3,
name: "party",
group: "fun",
filename: "party.png",
category: described_class::CATEGORY_CONFLICT_GROUP,
incoming_url: "/incoming.png",
existing_url: "/existing.png",
existing_group: "old",
upload_id: 42,
)
expect(described_class.from_h(row.to_h.as_json).to_h).to eq(row.to_h)
end
end
end