Files
warp_engine/spec/models/platform_link_spec.rb
T
mr.zeroandClaude Opus 5 e9594a663d One home per rule: error bodies, mime types, asset paths, ordering
Four small duplications, each of which had already drifted or was one edit away
from it.

**The JSON error body.** The host's `ApiController` was a line-by-line copy of
the engine's four `rescue_from` blocks plus `resolve_mime`. It is a
`WarpEngine::ApiErrorRendering` concern now, included by both, so "what a
failure looks like on the wire" is decided once.

**Mime resolution.** The same three lines lived in the engine's API controller
and the host's. `WarpEngine::Storage.mime_for` owns it — next to the adapter
that hands out the files.

**Finding the asset behind a path.** `DownloadService` and `FileService` each
escaped `%` and `_` by hand and ran their own `LIKE` — the second one without
the exact-match attempt the first one had. `ReleaseAsset.for_relative_path` is
the one lookup: the absolute path first, then a suffix match anchored at `/`
rather than the old `%path%`, which could match a different file whose name
merely contained this one. Two now-unreachable helpers
(`DownloadService.base_path`, `FileService.base_path`) go with it.

**Ordering.** `PlatformLink` and `SoftwareImage` ordered inside their
`default_scope`, which leaks into every association and aggregate — the proof
was already in the tree: `admin/images.rb` had to write
`SoftwareImage.unscope(:order).distinct.pluck(:image_id)`, because MySQL will
not order a DISTINCT by a column it does not select. Ordering is a scope you
ask for now (`ordered`), the two places that need it ask (`for_platform`, the
`software_images` association, so the API's image order is unchanged), and the
`unscope` is gone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-23 09:01:53 +02:00

63 lines
2.0 KiB
Ruby

require "rails_helper"
RSpec.describe WarpEngine::PlatformLink, type: :model do
describe "validations" do
it { should validate_presence_of(:name) }
it { should validate_presence_of(:url) }
it { should validate_presence_of(:platform) }
it "rejects invalid platform" do
link = build(:platform_link, platform: "invalid")
expect(link).not_to be_valid
expect(link.errors[:platform]).to be_present
end
it "accepts valid platforms" do
WarpEngine::PlatformLink::SUPPORTED_PLATFORMS.each do |p|
link = build(:platform_link, platform: p)
expect(link).to be_valid
end
end
end
describe "default scope" do
it "excludes soft-deleted records" do
active = create(:platform_link)
create(:platform_link, deleted_at: Time.current)
expect(WarpEngine::PlatformLink.all).to eq([active])
end
it "does not order: ordering is asked for, not inherited" do
second = create(:platform_link, position: 2)
first = create(:platform_link, position: 1)
expect(WarpEngine::PlatformLink.ordered).to eq([ first, second ])
expect(WarpEngine::PlatformLink.all.to_sql).not_to include("ORDER BY")
end
end
describe ".ordered" do
it "is what .for_platform uses, so the API keeps its link order" do
second = create(:platform_link, platform: "tic80", position: 2)
first = create(:platform_link, platform: "tic80", position: 1)
expect(WarpEngine::PlatformLink.for_platform("tic80")).to eq([ first, second ])
end
end
describe ".for_platform" do
it "returns links for given platform only" do
tic80_link = create(:platform_link, platform: "tic80")
create(:platform_link, platform: "love")
result = WarpEngine::PlatformLink.for_platform("tic80")
expect(result).to eq([tic80_link])
end
it "returns empty array for platform without links" do
expect(WarpEngine::PlatformLink.for_platform("godot")).to eq([])
end
end
end