feat: expose runtime OpenAPI contract

Register typed API operations and serve an always-current OpenAPI document without a generation command or committed generated artifact.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Daniel Noulet 2026-10-01 18:54:02 +02:00
parent 314c783659
commit 045f0b7cf2
9 changed files with 533 additions and 76 deletions

View file

@ -0,0 +1,48 @@
package openapi_test
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/danyel/go-guess/backend/internal/web/handler"
"github.com/danyel/go-guess/backend/internal/web/router"
)
func TestRuntimeContractContainsRegisteredTypedRoutes(t *testing.T) {
api := router.New(
handler.New(nil, nil, nil, nil, nil, nil, nil, nil, 10, "http://localhost:5173"),
nil,
"http://localhost:5173",
)
response := httptest.NewRecorder()
api.ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/api/openapi.json", nil))
if response.Code != http.StatusOK {
t.Fatalf("expected 200, got %d: %s", response.Code, response.Body.String())
}
var document map[string]any
if err := json.NewDecoder(response.Body).Decode(&document); err != nil {
t.Fatal(err)
}
if document["openapi"] != "3.0.3" {
t.Fatalf("unexpected OpenAPI version: %v", document["openapi"])
}
paths := document["paths"].(map[string]any)
if len(paths) < 30 {
t.Fatalf("contract contains only %d paths", len(paths))
}
jobs := paths["/api/jobs"].(map[string]any)
createJob := jobs["post"].(map[string]any)
if _, ok := createJob["requestBody"]; !ok {
t.Fatal("create-job request schema is missing")
}
if _, ok := createJob["security"]; !ok {
t.Fatal("create-job bearer security is missing")
}
contract := paths["/api/openapi.json"].(map[string]any)["get"].(map[string]any)
if parameters := contract["parameters"].([]any); len(parameters) != 0 {
t.Fatalf("contract endpoint unexpectedly requires parameters: %#v", parameters)
}
}