Files
warp_engine/spec/lib/storage_spec.rb
T
mr.zero 03a51a5b30 warp_engine 0.2.0: pluggable storage adapter and publish notifications
Two seams the hosts needed, both backward compatible.

Storage: artifacts are served through WarpEngine::Storage.adapter instead of
raw filesystem calls. The default :local adapter keeps the previous behaviour
byte for byte, including the path traversal guard. A host can now set
config.storage_adapter to any object answering file?/directory?/locate and
serve builds from an object store - FileService and /api/download both honour
a Location.redirect, so a signing adapter turns them into redirects.
DownloadService#create still returns an absolute path (nil when missing) for
existing callers; #locate is the new entry point that can also return a
redirect. Ingestion (upload, extraction, file manager) stays local for now.

Publish: PublishService emits ActiveSupport::Notifications
("warp_engine.publish") with platform/name/version/software/release, so hosts
can react to a new build without hanging callbacks on the models.
WarpEngine.instruments_publish? lets a host feature-detect and keep its
fallback for older engine versions.
2026-08-10 11:16:17 +02:00

60 lines
1.8 KiB
Ruby

require "rails_helper"
require "tmpdir"
RSpec.describe WarpEngine::Storage do
let(:tmpdir) { Dir.mktmpdir }
before do
allow(WarpEngine.config).to receive(:file_container_path).and_return(tmpdir)
WarpEngine::Storage.reset!
end
after { FileUtils.rm_rf(tmpdir) }
describe ".adapter" do
it "defaults to the local filesystem" do
allow(WarpEngine.config).to receive(:storage_adapter).and_return(:local)
expect(described_class.adapter).to be_a(described_class::LocalAdapter)
expect(described_class).to be_local
end
it "returns whatever object the host configured" do
custom = Object.new
allow(WarpEngine.config).to receive(:storage_adapter).and_return(custom)
expect(described_class.adapter).to eq(custom)
expect(described_class).not_to be_local
end
end
describe described_class::LocalAdapter do
subject(:adapter) { described_class.new }
before { File.write(File.join(tmpdir, "game-1.0.zip"), "zip") }
it "sees files and directories under the container" do
FileUtils.mkdir_p(File.join(tmpdir, "game-1.0"))
expect(adapter.file?("game-1.0.zip")).to be(true)
expect(adapter.directory?("game-1.0")).to be(true)
expect(adapter.file?("missing.zip")).to be(false)
end
# The path traversal guard has to survive the move behind the adapter.
it "refuses paths escaping the container" do
outside = File.join(Dir.mktmpdir, "secret.txt")
File.write(outside, "nope")
expect(adapter.file?("../#{File.basename(File.dirname(outside))}/secret.txt")).to be(false)
end
it "locates a file as an absolute path" do
location = adapter.locate("game-1.0.zip")
expect(location).to be_file
expect(location.path).to eq(File.join(tmpdir, "game-1.0.zip"))
end
end
end