Files
warp_engine/spec/requests/ci_controller_spec.rb
mr.zeroandClaude Opus 5 19d142ef64 WarpEngine 0.7.0: a képtár és a CI is a hoszté, adapteren keresztül
Két dolog volt beépítve az engine-be, ami nem az övé.

A **képtár** eddig `WarpEngine::Image` volt, pedig a modell teljesen általános:
a teletypegames-ben a tagok arcképét is ez hordozza, nem csak a katalógus
borítóit. Az `Image` modell, a feltöltött fájlok, az `/api/image/:id` végpont
és az admin oldal ezért átkerült a hosztba, az engine pedig adapteren szól
hozzá (`WarpEngine::Images`): `url_for` adja a katalógus JSON `imageUrl`-jét,
`select_options` a software-form képválasztóját, `build_from_upload` a
"tölts fel új képet" ágat. Az alapértelmezés az `Image` osztály, tehát a
default útvonal bitre a régi. A `SoftwareImage` (a katalógus-kapcsolat)
maradt az engine-ben, és **az `images` tábla nem mozdult**: az engine csak
abbahagyta a létrehozását, a generátor írja meg hoszt-kódként.

A **CI** eddig végig Woodpecker volt: kliens, aláírás-ellenőrzés,
pipeline-receptek, repo-szinkron, secret-kiosztás. Mindez egy adapter mögé
került (`WarpEngine.ci`), a Woodpecker-implementáció pedig az engine-ben
maradt `WarpEngine::CI::Woodpecker` néven — kliens, adapter, httpsig-ellenőrző
és a platformonkénti pipeline-receptek, mert a YAML-dialektus a szolgáltatóé.
Az engine saját kódja már nem nevez szolgáltatót: `CI::Repo` és `CI::Run`
értékeket kap, `CI::ConnectionError`/`ApiError`/`NotConfigured` hibákat dob, a
`Pipeline` pedig `remote_repo_id`-t ad a történelmi `woodpecker_repo_id`
kolumna fölött (a tábla itt sem mozdult). `c.ci_adapter = :none` azt jelenti,
hogy ez a hoszt nem buildel: az `/api/ci/*` 503, a `/build/config` elutasít,
az admin akciók elbújnak.

Mindkét seam a hoszt initializerében van kimondva, nem alapértelmezésre
hagyva — a hoszt megnevezi, mi a képtára és mi a CI-ja.

Törés a 0.6-hoz képest: `image_container_path`, `image_owners`,
`ci_platforms`, `ci_update_server`, `ci_extension_public_key(_url)`,
`woodpecker_url`, `woodpecker_api_token`, `woodpecker_repo_owner` és a
`WarpEngine.woodpecker_configured?` megszűnt; a helyük `c.image_class_name` /
`c.image_adapter` és `c.ci_adapter`. Az `/api/ci/*` `latest_run`/`trigger`
válasza a normalizált `CI::Run` alakot adja (number, status, branch, message,
createdAt, url), a `pipelines` lista pedig `repo_id`-t is közöl a megtartott
`woodpecker_repo_id` mellett.

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

82 lines
2.7 KiB
Ruby

require "rails_helper"
RSpec.describe "CI API", type: :request do
let(:ci) { instance_double(WarpEngine::CI::Woodpecker::Adapter, configured?: true, name: "Woodpecker") }
before do
allow(WarpEngine).to receive(:ci).and_return(ci)
allow(WarpEngine.config).to receive(:update_secret).and_return("s3cret")
end
describe "GET /api/ci/pipelines" do
it "returns active repos" do
create(:pipeline, repo_name: "mygame", platform: "tic80")
get "/api/ci/pipelines"
expect(response).to have_http_status(:ok)
json = JSON.parse(response.body)
expect(json.size).to eq(1)
expect(json.first["repo_name"]).to eq("mygame")
expect(json.first["repo_id"]).to eq(json.first["woodpecker_repo_id"])
end
it "returns 503 when no CI provider is configured" do
allow(ci).to receive(:configured?).and_return(false)
get "/api/ci/pipelines"
expect(response).to have_http_status(:service_unavailable)
end
end
describe "GET /api/ci/pipelines/:id/status" do
it "returns the repo with its latest run" do
repo = create(:pipeline, repo_owner: "org", repo_name: "game")
allow(ci).to receive(:run).with(repo.remote_repo_id, "latest")
.and_return(WarpEngine::CI::Run.new(number: 1, status: "success"))
get "/api/ci/pipelines/#{repo.id}/status"
expect(response).to have_http_status(:ok)
json = JSON.parse(response.body)
expect(json["pipeline"]["repo_name"]).to eq("game")
expect(json["latest_run"]["status"]).to eq("success")
end
it "answers without a run when the provider is unreachable" do
repo = create(:pipeline)
allow(ci).to receive(:run).and_raise(WarpEngine::CI::ConnectionError, "down")
get "/api/ci/pipelines/#{repo.id}/status"
expect(response).to have_http_status(:ok)
expect(JSON.parse(response.body)["latest_run"]).to be_nil
end
end
describe "POST /api/ci/pipelines/:id/trigger" do
it "requires authentication" do
repo = create(:pipeline)
post "/api/ci/pipelines/#{repo.id}/trigger"
expect(response).to have_http_status(:unauthorized)
end
it "triggers a pipeline with a valid secret" do
repo = create(:pipeline, repo_owner: "org", repo_name: "game")
allow(ci).to receive(:trigger).with(repo.remote_repo_id, branch: "main")
.and_return(WarpEngine::CI::Run.new(number: 7, status: "pending"))
post "/api/ci/pipelines/#{repo.id}/trigger",
headers: { "X-Update-Secret" => "s3cret" }
expect(response).to have_http_status(:ok)
json = JSON.parse(response.body)
expect(json["triggered"]).to be true
expect(json["pipeline"]["number"]).to eq(7)
end
end
end