dev-pod-api-build/internal/api/runner_callback_test.go
2026-04-16 04:16:36 +00:00

132 lines
3.9 KiB
Go

package api
import (
"net/http"
"testing"
"github.com/iliaivanov/spec-kit-remote/cmd/dev-pod-api/internal/model"
)
func TestRunnerCallback_Success(t *testing.T) {
_, _, rs, _, router := newRunnerTestRouter()
rs.mu.Lock()
rs.runners["r1"] = &model.Runner{ID: "r1", User: "alice", Status: model.RunnerStatusPodCreating}
rs.mu.Unlock()
body := `{"status":"runner_registered","forgejo_runner_id":"fj-42"}`
rr := doRequest(router, http.MethodPost, "/internal/runners/r1/callback", body, "")
if rr.Code != http.StatusOK {
t.Fatalf("expected 200, got %d: %s", rr.Code, rr.Body.String())
}
var runner model.Runner
decodeJSON(t, rr, &runner)
if runner.Status != model.RunnerStatusRunnerRegistered {
t.Errorf("expected status runner_registered, got %s", runner.Status)
}
if runner.ForgejoRunnerID != "fj-42" {
t.Errorf("expected forgejo_runner_id fj-42, got %s", runner.ForgejoRunnerID)
}
}
func TestRunnerCallback_NoAuth(t *testing.T) {
_, _, rs, _, router := newRunnerTestRouter()
rs.mu.Lock()
rs.runners["r1"] = &model.Runner{ID: "r1", User: "alice", Status: model.RunnerStatusPodCreating}
rs.mu.Unlock()
body := `{"status":"runner_registered"}`
rr := doRequest(router, http.MethodPost, "/internal/runners/r1/callback", body, "")
if rr.Code != http.StatusOK {
t.Fatalf("internal callback should work without auth, got %d: %s", rr.Code, rr.Body.String())
}
}
func TestRunnerCallback_NotFound(t *testing.T) {
_, _, _, _, router := newRunnerTestRouter()
body := `{"status":"runner_registered"}`
rr := doRequest(router, http.MethodPost, "/internal/runners/nonexistent/callback", body, "")
if rr.Code != http.StatusNotFound {
t.Fatalf("expected 404, got %d: %s", rr.Code, rr.Body.String())
}
}
func TestRunnerCallback_InvalidTransition(t *testing.T) {
_, _, rs, _, router := newRunnerTestRouter()
rs.mu.Lock()
rs.runners["r1"] = &model.Runner{ID: "r1", User: "alice", Status: model.RunnerStatusReceived}
rs.mu.Unlock()
body := `{"status":"completed"}`
rr := doRequest(router, http.MethodPost, "/internal/runners/r1/callback", body, "")
if rr.Code != http.StatusBadRequest {
t.Fatalf("expected 400 for invalid transition, got %d: %s", rr.Code, rr.Body.String())
}
}
func TestRunnerCallback_InvalidBody(t *testing.T) {
_, _, rs, _, router := newRunnerTestRouter()
rs.mu.Lock()
rs.runners["r1"] = &model.Runner{ID: "r1", User: "alice", Status: model.RunnerStatusPodCreating}
rs.mu.Unlock()
rr := doRequest(router, http.MethodPost, "/internal/runners/r1/callback", "not json", "")
if rr.Code != http.StatusBadRequest {
t.Fatalf("expected 400, got %d: %s", rr.Code, rr.Body.String())
}
}
func TestRunnerCallback_CompletedStatus(t *testing.T) {
_, _, rs, _, router := newRunnerTestRouter()
rs.mu.Lock()
rs.runners["r1"] = &model.Runner{ID: "r1", User: "alice", Status: model.RunnerStatusJobClaimed}
rs.mu.Unlock()
body := `{"status":"completed"}`
rr := doRequest(router, http.MethodPost, "/internal/runners/r1/callback", body, "")
if rr.Code != http.StatusOK {
t.Fatalf("expected 200, got %d: %s", rr.Code, rr.Body.String())
}
var runner model.Runner
decodeJSON(t, rr, &runner)
if runner.Status != model.RunnerStatusCompleted {
t.Errorf("expected status completed, got %s", runner.Status)
}
if runner.CompletedAt == nil {
t.Error("expected completed_at to be set")
}
}
func TestRunnerCallback_FailedStatus(t *testing.T) {
_, _, rs, _, router := newRunnerTestRouter()
rs.mu.Lock()
rs.runners["r1"] = &model.Runner{ID: "r1", User: "alice", Status: model.RunnerStatusRunnerRegistered}
rs.mu.Unlock()
body := `{"status":"failed"}`
rr := doRequest(router, http.MethodPost, "/internal/runners/r1/callback", body, "")
if rr.Code != http.StatusOK {
t.Fatalf("expected 200, got %d: %s", rr.Code, rr.Body.String())
}
var runner model.Runner
decodeJSON(t, rr, &runner)
if runner.Status != model.RunnerStatusFailed {
t.Errorf("expected status failed, got %s", runner.Status)
}
}