OTO Server가 runner 등록 상태를 직접 소유하고 Dart runner가 Server registration 계약으로 전환되어야 Control Plane 분리 마이그레이션을 이어갈 수 있다.
170 lines
5.2 KiB
Go
170 lines
5.2 KiB
Go
package httpserver
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/toki/oto/services/core/internal/runnerregistry"
|
|
otopb "github.com/toki/oto/services/core/oto"
|
|
)
|
|
|
|
func TestHandleHealthz(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, "/healthz", nil)
|
|
rr := httptest.NewRecorder()
|
|
|
|
handleHealthz(rr, req)
|
|
|
|
if status := rr.Code; status != http.StatusOK {
|
|
t.Errorf("handleHealthz returned wrong status code: got %v want %v", status, http.StatusOK)
|
|
}
|
|
|
|
expected := "OK"
|
|
if body := rr.Body.String(); body != expected {
|
|
t.Errorf("handleHealthz returned unexpected body: got %v want %v", body, expected)
|
|
}
|
|
}
|
|
|
|
func TestHandleHealthz_MethodNotAllowed(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodPost, "/healthz", nil)
|
|
rr := httptest.NewRecorder()
|
|
|
|
handleHealthz(rr, req)
|
|
|
|
if status := rr.Code; status != http.StatusMethodNotAllowed {
|
|
t.Errorf("handleHealthz for POST returned wrong status code: got %v want %v", status, http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
func TestHandleReadyz(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, "/readyz", nil)
|
|
rr := httptest.NewRecorder()
|
|
|
|
handleReadyz(rr, req)
|
|
|
|
if status := rr.Code; status != http.StatusOK {
|
|
t.Errorf("handleReadyz returned wrong status code: got %v want %v", status, http.StatusOK)
|
|
}
|
|
|
|
expected := "OK"
|
|
if body := rr.Body.String(); body != expected {
|
|
t.Errorf("handleReadyz returned unexpected body: got %v want %v", body, expected)
|
|
}
|
|
}
|
|
|
|
func TestHandleReadyz_MethodNotAllowed(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodPost, "/readyz", nil)
|
|
rr := httptest.NewRecorder()
|
|
|
|
handleReadyz(rr, req)
|
|
|
|
if status := rr.Code; status != http.StatusMethodNotAllowed {
|
|
t.Errorf("handleReadyz for POST returned wrong status code: got %v want %v", status, http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
func TestServerMux(t *testing.T) {
|
|
registry := runnerregistry.New()
|
|
server := NewServerWithRegistry(":0", registry)
|
|
mux := server.httpServer.Handler.(*http.ServeMux)
|
|
|
|
// Test healthz routing
|
|
reqHealth := httptest.NewRequest(http.MethodGet, "/healthz", nil)
|
|
rrHealth := httptest.NewRecorder()
|
|
mux.ServeHTTP(rrHealth, reqHealth)
|
|
if rrHealth.Code != http.StatusOK {
|
|
t.Errorf("mux /healthz failed: got status %v", rrHealth.Code)
|
|
}
|
|
|
|
// Test readyz routing
|
|
reqReady := httptest.NewRequest(http.MethodGet, "/readyz", nil)
|
|
rrReady := httptest.NewRecorder()
|
|
mux.ServeHTTP(rrReady, reqReady)
|
|
if rrReady.Code != http.StatusOK {
|
|
t.Errorf("mux /readyz failed: got status %v", rrReady.Code)
|
|
}
|
|
|
|
// Test fallback/not found routing
|
|
reqNotFound := httptest.NewRequest(http.MethodGet, "/unknown", nil)
|
|
rrNotFound := httptest.NewRecorder()
|
|
mux.ServeHTTP(rrNotFound, reqNotFound)
|
|
if rrNotFound.Code != http.StatusNotFound {
|
|
t.Errorf("mux /unknown routing status code: got %v want %v", rrNotFound.Code, http.StatusNotFound)
|
|
}
|
|
}
|
|
|
|
func TestHandleRunnerRegisterAcceptsAndStoresRunner(t *testing.T) {
|
|
registry := runnerregistry.New()
|
|
body := bytes.NewBufferString(`{
|
|
"enrollment_token":"token-123",
|
|
"runner_id":"runner-123",
|
|
"alias":"linux-build",
|
|
"protocol_version":"oto.runner.v1",
|
|
"capability":{"name":"oto-runner","version":"1.0.0"},
|
|
"command_catalog":{"command_types":["Shell","Git"]}
|
|
}`)
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/runners/register", body)
|
|
rr := httptest.NewRecorder()
|
|
|
|
handleRunnerRegister(registry)(rr, req)
|
|
|
|
if rr.Code != http.StatusOK {
|
|
t.Fatalf("status = %v, want %v; body=%s", rr.Code, http.StatusOK, rr.Body.String())
|
|
}
|
|
var response otopb.RegisterRunnerResponse
|
|
if err := json.Unmarshal(rr.Body.Bytes(), &response); err != nil {
|
|
t.Fatalf("decode response: %v", err)
|
|
}
|
|
if !response.GetAccepted() {
|
|
t.Fatalf("accepted=false, reason=%q", response.GetRejectReason())
|
|
}
|
|
record, ok := registry.Snapshot("runner-123")
|
|
if !ok {
|
|
t.Fatal("registered runner was not stored")
|
|
}
|
|
if record.Alias != "linux-build" {
|
|
t.Fatalf("alias = %q, want linux-build", record.Alias)
|
|
}
|
|
if got := record.CommandTypes; len(got) != 2 || got[0] != "Shell" || got[1] != "Git" {
|
|
t.Fatalf("command types = %#v, want [Shell Git]", got)
|
|
}
|
|
}
|
|
|
|
func TestHandleRunnerRegisterRejectsMissingToken(t *testing.T) {
|
|
registry := runnerregistry.New()
|
|
body := bytes.NewBufferString(`{
|
|
"runner_id":"runner-123",
|
|
"protocol_version":"oto.runner.v1"
|
|
}`)
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/runners/register", body)
|
|
rr := httptest.NewRecorder()
|
|
|
|
handleRunnerRegister(registry)(rr, req)
|
|
|
|
if rr.Code != http.StatusOK {
|
|
t.Fatalf("status = %v, want %v", rr.Code, http.StatusOK)
|
|
}
|
|
var response otopb.RegisterRunnerResponse
|
|
if err := json.Unmarshal(rr.Body.Bytes(), &response); err != nil {
|
|
t.Fatalf("decode response: %v", err)
|
|
}
|
|
if response.GetAccepted() {
|
|
t.Fatal("missing token registration was accepted")
|
|
}
|
|
if response.GetRejectReason() != "missing enrollment token" {
|
|
t.Fatalf("reject reason = %q, want missing enrollment token", response.GetRejectReason())
|
|
}
|
|
}
|
|
|
|
func TestHandleRunnerRegisterMethodNotAllowed(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/runners/register", nil)
|
|
rr := httptest.NewRecorder()
|
|
|
|
handleRunnerRegister(runnerregistry.New())(rr, req)
|
|
|
|
if rr.Code != http.StatusMethodNotAllowed {
|
|
t.Fatalf("status = %v, want %v", rr.Code, http.StatusMethodNotAllowed)
|
|
}
|
|
}
|