build source
This commit is contained in:
commit
ee1fec43ed
4171 changed files with 1351288 additions and 0 deletions
548
internal/api/runners_test.go
Normal file
548
internal/api/runners_test.go
Normal file
|
|
@ -0,0 +1,548 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/iliaivanov/spec-kit-remote/cmd/dev-pod-api/internal/k8s"
|
||||
"github.com/iliaivanov/spec-kit-remote/cmd/dev-pod-api/internal/model"
|
||||
"github.com/iliaivanov/spec-kit-remote/cmd/dev-pod-api/internal/store"
|
||||
)
|
||||
|
||||
type mockRunnerStore struct {
|
||||
mu sync.Mutex
|
||||
runners map[string]*model.Runner
|
||||
}
|
||||
|
||||
func newMockRunnerStore() *mockRunnerStore {
|
||||
return &mockRunnerStore{runners: make(map[string]*model.Runner)}
|
||||
}
|
||||
|
||||
func (m *mockRunnerStore) CreateRunner(_ context.Context, r *model.Runner) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if _, exists := m.runners[r.ID]; exists {
|
||||
return fmt.Errorf("runner %q: %w", r.ID, store.ErrDuplicate)
|
||||
}
|
||||
for _, existing := range m.runners {
|
||||
if r.WebhookDeliveryID != "" && existing.WebhookDeliveryID == r.WebhookDeliveryID {
|
||||
return fmt.Errorf("runner %q: %w", r.ID, store.ErrDuplicate)
|
||||
}
|
||||
}
|
||||
copy := *r
|
||||
m.runners[r.ID] = ©
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockRunnerStore) GetRunner(_ context.Context, id string) (*model.Runner, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
r, ok := m.runners[id]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("runner %q: %w", id, store.ErrNotFound)
|
||||
}
|
||||
copy := *r
|
||||
return ©, nil
|
||||
}
|
||||
|
||||
func (m *mockRunnerStore) ListRunners(_ context.Context, userFilter, statusFilter string) ([]model.Runner, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
var result []model.Runner
|
||||
for _, r := range m.runners {
|
||||
if userFilter != "" && r.User != userFilter {
|
||||
continue
|
||||
}
|
||||
if statusFilter != "" && string(r.Status) != statusFilter {
|
||||
continue
|
||||
}
|
||||
result = append(result, *r)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (m *mockRunnerStore) UpdateRunnerStatus(_ context.Context, id string, newStatus model.RunnerStatus, forgejoRunnerID string) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
r, ok := m.runners[id]
|
||||
if !ok {
|
||||
return fmt.Errorf("runner %q: %w", id, store.ErrNotFound)
|
||||
}
|
||||
if !r.Status.CanTransitionTo(newStatus) {
|
||||
return fmt.Errorf("invalid transition from %s to %s", r.Status, newStatus)
|
||||
}
|
||||
r.Status = newStatus
|
||||
if forgejoRunnerID != "" {
|
||||
r.ForgejoRunnerID = forgejoRunnerID
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
if newStatus == model.RunnerStatusJobClaimed {
|
||||
r.ClaimedAt = &now
|
||||
}
|
||||
if newStatus.IsTerminal() {
|
||||
r.CompletedAt = &now
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockRunnerStore) DeleteRunner(_ context.Context, id string) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if _, ok := m.runners[id]; !ok {
|
||||
return fmt.Errorf("runner %q: %w", id, store.ErrNotFound)
|
||||
}
|
||||
delete(m.runners, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockRunnerStore) IsDeliveryProcessed(_ context.Context, deliveryID string) (bool, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if deliveryID == "" {
|
||||
return false, nil
|
||||
}
|
||||
for _, r := range m.runners {
|
||||
if r.WebhookDeliveryID == deliveryID {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (m *mockRunnerStore) GetStaleRunners(_ context.Context, ttl time.Duration) ([]model.Runner, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
cutoff := time.Now().UTC().Add(-ttl)
|
||||
var result []model.Runner
|
||||
for _, r := range m.runners {
|
||||
if r.CreatedAt.Before(cutoff) && !r.Status.IsTerminal() && r.Status != model.RunnerStatusCleanupPending {
|
||||
result = append(result, *r)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type mockRunnerPodManager struct {
|
||||
mu sync.Mutex
|
||||
pods map[string]string // runnerID -> podName
|
||||
createErr error
|
||||
}
|
||||
|
||||
func newMockRunnerPodManager() *mockRunnerPodManager {
|
||||
return &mockRunnerPodManager{pods: make(map[string]string)}
|
||||
}
|
||||
|
||||
func (m *mockRunnerPodManager) CreateRunnerPod(_ context.Context, opts k8s.CreateRunnerPodOpts) (string, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.createErr != nil {
|
||||
return "", m.createErr
|
||||
}
|
||||
podName := model.RunnerPodName(opts.RunnerID)
|
||||
m.pods[opts.RunnerID] = podName
|
||||
return podName, nil
|
||||
}
|
||||
|
||||
func (m *mockRunnerPodManager) DeleteRunnerPod(_ context.Context, _, podName string) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for k, v := range m.pods {
|
||||
if v == podName {
|
||||
delete(m.pods, k)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newRunnerTestRouter() (*mockKeyValidator, *mockUserStore, *mockRunnerStore, *mockRunnerPodManager, http.Handler) {
|
||||
kv := newMockValidator()
|
||||
us := newMockUserStore()
|
||||
rs := newMockRunnerStore()
|
||||
rp := newMockRunnerPodManager()
|
||||
fm := newMockForgejoManager()
|
||||
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||
srv := &Server{
|
||||
Store: kv,
|
||||
K8s: newMockPodManager(),
|
||||
Users: us,
|
||||
Usage: newMockUsageStore(),
|
||||
Runners: rs,
|
||||
RunnerPods: rp,
|
||||
Forgejo: fm,
|
||||
Logger: logger,
|
||||
GenerateKey: mockGenerateKey,
|
||||
}
|
||||
return kv, us, rs, rp, NewRouter(srv)
|
||||
}
|
||||
|
||||
func TestCreateRunner_Success(t *testing.T) {
|
||||
kv, us, rs, rp, router := newRunnerTestRouter()
|
||||
user := &model.User{ID: "alice", Quota: model.DefaultQuota()}
|
||||
kv.addKey("dpk_test", user, model.RoleUser)
|
||||
us.addUser(user)
|
||||
|
||||
body := `{"user":"alice","repo":"alice/myrepo","branch":"main","tools":"go","task":"build it"}`
|
||||
rr := doRequest(router, http.MethodPost, "/api/v1/runners", body, "dpk_test")
|
||||
|
||||
if rr.Code != http.StatusCreated {
|
||||
t.Fatalf("expected 201, got %d: %s", rr.Code, rr.Body.String())
|
||||
}
|
||||
|
||||
var runner model.Runner
|
||||
decodeJSON(t, rr, &runner)
|
||||
if runner.User != "alice" {
|
||||
t.Errorf("expected user alice, got %s", runner.User)
|
||||
}
|
||||
if runner.RepoURL != "alice/myrepo" {
|
||||
t.Errorf("expected repo alice/myrepo, got %s", runner.RepoURL)
|
||||
}
|
||||
if runner.Status != model.RunnerStatusPodCreating {
|
||||
t.Errorf("expected status pod_creating, got %s", runner.Status)
|
||||
}
|
||||
|
||||
rs.mu.Lock()
|
||||
count := len(rs.runners)
|
||||
rs.mu.Unlock()
|
||||
if count != 1 {
|
||||
t.Errorf("expected 1 runner in store, got %d", count)
|
||||
}
|
||||
|
||||
rp.mu.Lock()
|
||||
podCount := len(rp.pods)
|
||||
rp.mu.Unlock()
|
||||
if podCount != 1 {
|
||||
t.Errorf("expected 1 pod created, got %d", podCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateRunner_DedupeWebhook(t *testing.T) {
|
||||
kv, us, rs, _, router := newRunnerTestRouter()
|
||||
user := &model.User{ID: "alice", Quota: model.DefaultQuota()}
|
||||
kv.addKey("dpk_test", user, model.RoleUser)
|
||||
us.addUser(user)
|
||||
|
||||
body := `{"user":"alice","repo":"alice/myrepo","webhook_delivery_id":"delivery-xyz"}`
|
||||
rr := doRequest(router, http.MethodPost, "/api/v1/runners", body, "dpk_test")
|
||||
if rr.Code != http.StatusCreated {
|
||||
t.Fatalf("first create: expected 201, got %d: %s", rr.Code, rr.Body.String())
|
||||
}
|
||||
|
||||
rr2 := doRequest(router, http.MethodPost, "/api/v1/runners", body, "dpk_test")
|
||||
if rr2.Code != http.StatusConflict {
|
||||
t.Fatalf("dupe: expected 409, got %d: %s", rr2.Code, rr2.Body.String())
|
||||
}
|
||||
|
||||
rs.mu.Lock()
|
||||
count := len(rs.runners)
|
||||
rs.mu.Unlock()
|
||||
if count != 1 {
|
||||
t.Errorf("expected exactly 1 runner after dedupe, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateRunner_ForbiddenOtherUser(t *testing.T) {
|
||||
kv, us, _, _, router := newRunnerTestRouter()
|
||||
user := &model.User{ID: "alice", Quota: model.DefaultQuota()}
|
||||
kv.addKey("dpk_test", user, model.RoleUser)
|
||||
us.addUser(user)
|
||||
us.addUser(&model.User{ID: "bob", Quota: model.DefaultQuota()})
|
||||
|
||||
body := `{"user":"bob","repo":"bob/repo"}`
|
||||
rr := doRequest(router, http.MethodPost, "/api/v1/runners", body, "dpk_test")
|
||||
if rr.Code != http.StatusForbidden {
|
||||
t.Fatalf("expected 403, got %d: %s", rr.Code, rr.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateRunner_AdminCanCreateForOthers(t *testing.T) {
|
||||
kv, us, _, _, router := newRunnerTestRouter()
|
||||
admin := &model.User{ID: "admin-user", Quota: model.DefaultQuota()}
|
||||
bob := &model.User{ID: "bob", Quota: model.DefaultQuota()}
|
||||
kv.addKey("dpk_admin", admin, model.RoleAdmin)
|
||||
us.addUser(admin)
|
||||
us.addUser(bob)
|
||||
|
||||
body := `{"user":"bob","repo":"bob/repo"}`
|
||||
rr := doRequest(router, http.MethodPost, "/api/v1/runners", body, "dpk_admin")
|
||||
if rr.Code != http.StatusCreated {
|
||||
t.Fatalf("expected 201, got %d: %s", rr.Code, rr.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateRunner_UserNotFound(t *testing.T) {
|
||||
kv, _, _, _, router := newRunnerTestRouter()
|
||||
user := &model.User{ID: "alice", Quota: model.DefaultQuota()}
|
||||
kv.addKey("dpk_test", user, model.RoleUser)
|
||||
|
||||
body := `{"user":"alice","repo":"alice/repo"}`
|
||||
rr := doRequest(router, http.MethodPost, "/api/v1/runners", body, "dpk_test")
|
||||
if rr.Code != http.StatusNotFound {
|
||||
t.Fatalf("expected 404, got %d: %s", rr.Code, rr.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateRunner_InvalidBody(t *testing.T) {
|
||||
kv, _, _, _, router := newRunnerTestRouter()
|
||||
user := &model.User{ID: "alice", Quota: model.DefaultQuota()}
|
||||
kv.addKey("dpk_test", user, model.RoleUser)
|
||||
|
||||
rr := doRequest(router, http.MethodPost, "/api/v1/runners", "not json", "dpk_test")
|
||||
if rr.Code != http.StatusBadRequest {
|
||||
t.Fatalf("expected 400, got %d", rr.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateRunner_ValidationError(t *testing.T) {
|
||||
kv, _, _, _, router := newRunnerTestRouter()
|
||||
user := &model.User{ID: "alice", Quota: model.DefaultQuota()}
|
||||
kv.addKey("dpk_test", user, model.RoleUser)
|
||||
|
||||
rr := doRequest(router, http.MethodPost, "/api/v1/runners", `{"user":"alice"}`, "dpk_test")
|
||||
if rr.Code != http.StatusBadRequest {
|
||||
t.Fatalf("expected 400, got %d: %s", rr.Code, rr.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateRunner_PodCreationFails(t *testing.T) {
|
||||
kv := newMockValidator()
|
||||
us := newMockUserStore()
|
||||
rs := newMockRunnerStore()
|
||||
rp := newMockRunnerPodManager()
|
||||
rp.createErr = fmt.Errorf("k8s error")
|
||||
fm := newMockForgejoManager()
|
||||
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||
srv := &Server{
|
||||
Store: kv,
|
||||
K8s: newMockPodManager(),
|
||||
Users: us,
|
||||
Usage: newMockUsageStore(),
|
||||
Runners: rs,
|
||||
RunnerPods: rp,
|
||||
Forgejo: fm,
|
||||
Logger: logger,
|
||||
GenerateKey: mockGenerateKey,
|
||||
}
|
||||
router := NewRouter(srv)
|
||||
|
||||
user := &model.User{ID: "alice", Quota: model.DefaultQuota()}
|
||||
kv.addKey("dpk_test", user, model.RoleUser)
|
||||
us.addUser(user)
|
||||
|
||||
body := `{"user":"alice","repo":"alice/repo"}`
|
||||
rr := doRequest(router, http.MethodPost, "/api/v1/runners", body, "dpk_test")
|
||||
if rr.Code != http.StatusInternalServerError {
|
||||
t.Fatalf("expected 500, got %d: %s", rr.Code, rr.Body.String())
|
||||
}
|
||||
|
||||
rs.mu.Lock()
|
||||
for _, r := range rs.runners {
|
||||
if r.Status != model.RunnerStatusFailed {
|
||||
t.Errorf("expected runner status failed after pod creation failure, got %s", r.Status)
|
||||
}
|
||||
}
|
||||
rs.mu.Unlock()
|
||||
}
|
||||
|
||||
func TestListRunners_UserSeesOwn(t *testing.T) {
|
||||
kv, us, rs, _, router := newRunnerTestRouter()
|
||||
alice := &model.User{ID: "alice", Quota: model.DefaultQuota()}
|
||||
kv.addKey("dpk_alice", alice, model.RoleUser)
|
||||
us.addUser(alice)
|
||||
|
||||
rs.mu.Lock()
|
||||
rs.runners["r1"] = &model.Runner{ID: "r1", User: "alice", Status: model.RunnerStatusReceived}
|
||||
rs.runners["r2"] = &model.Runner{ID: "r2", User: "bob", Status: model.RunnerStatusReceived}
|
||||
rs.mu.Unlock()
|
||||
|
||||
rr := doRequest(router, http.MethodGet, "/api/v1/runners", "", "dpk_alice")
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d: %s", rr.Code, rr.Body.String())
|
||||
}
|
||||
|
||||
var runners []model.Runner
|
||||
decodeJSON(t, rr, &runners)
|
||||
if len(runners) != 1 {
|
||||
t.Fatalf("expected 1 runner, got %d", len(runners))
|
||||
}
|
||||
if runners[0].User != "alice" {
|
||||
t.Errorf("expected alice's runner, got %s", runners[0].User)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListRunners_AdminSeesAll(t *testing.T) {
|
||||
kv, us, rs, _, router := newRunnerTestRouter()
|
||||
admin := &model.User{ID: "admin-user", Quota: model.DefaultQuota()}
|
||||
kv.addKey("dpk_admin", admin, model.RoleAdmin)
|
||||
us.addUser(admin)
|
||||
|
||||
rs.mu.Lock()
|
||||
rs.runners["r1"] = &model.Runner{ID: "r1", User: "alice", Status: model.RunnerStatusReceived}
|
||||
rs.runners["r2"] = &model.Runner{ID: "r2", User: "bob", Status: model.RunnerStatusReceived}
|
||||
rs.mu.Unlock()
|
||||
|
||||
rr := doRequest(router, http.MethodGet, "/api/v1/runners", "", "dpk_admin")
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d: %s", rr.Code, rr.Body.String())
|
||||
}
|
||||
|
||||
var runners []model.Runner
|
||||
decodeJSON(t, rr, &runners)
|
||||
if len(runners) != 2 {
|
||||
t.Fatalf("expected 2 runners, got %d", len(runners))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteRunner_Success(t *testing.T) {
|
||||
kv, us, rs, _, router := newRunnerTestRouter()
|
||||
user := &model.User{ID: "alice", Quota: model.DefaultQuota()}
|
||||
kv.addKey("dpk_test", user, model.RoleUser)
|
||||
us.addUser(user)
|
||||
|
||||
rs.mu.Lock()
|
||||
rs.runners["r1"] = &model.Runner{ID: "r1", User: "alice", PodName: "dev-pod-r1", Status: model.RunnerStatusReceived}
|
||||
rs.mu.Unlock()
|
||||
|
||||
rr := doRequest(router, http.MethodDelete, "/api/v1/runners/r1", "", "dpk_test")
|
||||
if rr.Code != http.StatusNoContent {
|
||||
t.Fatalf("expected 204, got %d: %s", rr.Code, rr.Body.String())
|
||||
}
|
||||
|
||||
rs.mu.Lock()
|
||||
count := len(rs.runners)
|
||||
rs.mu.Unlock()
|
||||
if count != 0 {
|
||||
t.Errorf("expected 0 runners after delete, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteRunner_NotFound(t *testing.T) {
|
||||
kv, _, _, _, router := newRunnerTestRouter()
|
||||
user := &model.User{ID: "alice", Quota: model.DefaultQuota()}
|
||||
kv.addKey("dpk_test", user, model.RoleUser)
|
||||
|
||||
rr := doRequest(router, http.MethodDelete, "/api/v1/runners/nonexistent", "", "dpk_test")
|
||||
if rr.Code != http.StatusNotFound {
|
||||
t.Fatalf("expected 404, got %d: %s", rr.Code, rr.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteRunner_ForbiddenOtherUser(t *testing.T) {
|
||||
kv, us, rs, _, router := newRunnerTestRouter()
|
||||
user := &model.User{ID: "alice", Quota: model.DefaultQuota()}
|
||||
kv.addKey("dpk_test", user, model.RoleUser)
|
||||
us.addUser(user)
|
||||
|
||||
rs.mu.Lock()
|
||||
rs.runners["r1"] = &model.Runner{ID: "r1", User: "bob", Status: model.RunnerStatusReceived}
|
||||
rs.mu.Unlock()
|
||||
|
||||
rr := doRequest(router, http.MethodDelete, "/api/v1/runners/r1", "", "dpk_test")
|
||||
if rr.Code != http.StatusForbidden {
|
||||
t.Fatalf("expected 403, got %d: %s", rr.Code, rr.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateRunnerStatus_Success(t *testing.T) {
|
||||
kv, us, rs, _, router := newRunnerTestRouter()
|
||||
user := &model.User{ID: "alice", Quota: model.DefaultQuota()}
|
||||
kv.addKey("dpk_test", user, model.RoleUser)
|
||||
us.addUser(user)
|
||||
|
||||
rs.mu.Lock()
|
||||
rs.runners["r1"] = &model.Runner{ID: "r1", User: "alice", Status: model.RunnerStatusReceived}
|
||||
rs.mu.Unlock()
|
||||
|
||||
body := `{"status":"pod_creating"}`
|
||||
rr := doRequest(router, http.MethodPost, "/api/v1/runners/r1/status", body, "dpk_test")
|
||||
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.RunnerStatusPodCreating {
|
||||
t.Errorf("expected status pod_creating, got %s", runner.Status)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateRunnerStatus_InvalidTransition(t *testing.T) {
|
||||
kv, us, rs, _, router := newRunnerTestRouter()
|
||||
user := &model.User{ID: "alice", Quota: model.DefaultQuota()}
|
||||
kv.addKey("dpk_test", user, model.RoleUser)
|
||||
us.addUser(user)
|
||||
|
||||
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, "/api/v1/runners/r1/status", body, "dpk_test")
|
||||
if rr.Code != http.StatusBadRequest {
|
||||
t.Fatalf("expected 400, got %d: %s", rr.Code, rr.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateRunnerStatus_NotFound(t *testing.T) {
|
||||
kv, _, _, _, router := newRunnerTestRouter()
|
||||
user := &model.User{ID: "alice", Quota: model.DefaultQuota()}
|
||||
kv.addKey("dpk_test", user, model.RoleUser)
|
||||
|
||||
body := `{"status":"pod_creating"}`
|
||||
rr := doRequest(router, http.MethodPost, "/api/v1/runners/nonexistent/status", body, "dpk_test")
|
||||
if rr.Code != http.StatusNotFound {
|
||||
t.Fatalf("expected 404, got %d: %s", rr.Code, rr.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateRunnerStatus_WithForgejoRunnerID(t *testing.T) {
|
||||
kv, us, rs, _, router := newRunnerTestRouter()
|
||||
user := &model.User{ID: "alice", Quota: model.DefaultQuota()}
|
||||
kv.addKey("dpk_test", user, model.RoleUser)
|
||||
us.addUser(user)
|
||||
|
||||
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":"forgejo-99"}`
|
||||
rr := doRequest(router, http.MethodPost, "/api/v1/runners/r1/status", body, "dpk_test")
|
||||
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.ForgejoRunnerID != "forgejo-99" {
|
||||
t.Errorf("expected forgejo_runner_id forgejo-99, got %s", runner.ForgejoRunnerID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunnerRoutes_NoAuth(t *testing.T) {
|
||||
_, _, _, _, router := newRunnerTestRouter()
|
||||
|
||||
routes := []struct {
|
||||
method string
|
||||
path string
|
||||
}{
|
||||
{http.MethodPost, "/api/v1/runners"},
|
||||
{http.MethodGet, "/api/v1/runners"},
|
||||
{http.MethodDelete, "/api/v1/runners/r1"},
|
||||
{http.MethodPost, "/api/v1/runners/r1/status"},
|
||||
}
|
||||
|
||||
for _, rt := range routes {
|
||||
t.Run(rt.method+" "+rt.path, func(t *testing.T) {
|
||||
rr := doRequest(router, rt.method, rt.path, "", "")
|
||||
if rr.Code != http.StatusUnauthorized {
|
||||
t.Fatalf("expected 401, got %d", rr.Code)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue