612 lines
19 KiB
Go
612 lines
19 KiB
Go
package internalhttp
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"galaxy/gamemaster/internal/api/internalhttp/handlers"
|
|
"galaxy/gamemaster/internal/domain/engineversion"
|
|
"galaxy/gamemaster/internal/domain/operation"
|
|
domainruntime "galaxy/gamemaster/internal/domain/runtime"
|
|
"galaxy/gamemaster/internal/service/adminbanish"
|
|
"galaxy/gamemaster/internal/service/adminforce"
|
|
"galaxy/gamemaster/internal/service/adminpatch"
|
|
"galaxy/gamemaster/internal/service/adminstop"
|
|
"galaxy/gamemaster/internal/service/commandexecute"
|
|
engineversionsvc "galaxy/gamemaster/internal/service/engineversion"
|
|
"galaxy/gamemaster/internal/service/livenessreply"
|
|
"galaxy/gamemaster/internal/service/orderput"
|
|
"galaxy/gamemaster/internal/service/registerruntime"
|
|
"galaxy/gamemaster/internal/service/reportget"
|
|
"galaxy/gamemaster/internal/service/turngeneration"
|
|
|
|
"github.com/getkin/kin-openapi/openapi3"
|
|
"github.com/getkin/kin-openapi/openapi3filter"
|
|
"github.com/getkin/kin-openapi/routers"
|
|
"github.com/getkin/kin-openapi/routers/legacy"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestInternalRESTConformance loads the OpenAPI specification, drives
|
|
// every internal REST operation against the live listener backed by
|
|
// stub services, and validates each request and response body
|
|
// against the spec via `openapi3filter.ValidateRequest` and
|
|
// `openapi3filter.ValidateResponse`. Failure-path response shapes
|
|
// are intentionally out of scope here; per-handler tests under
|
|
// `handlers/<op>_test.go` cover the failure branches.
|
|
func TestInternalRESTConformance(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
doc := loadConformanceSpec(t)
|
|
|
|
router, err := legacy.NewRouter(doc)
|
|
require.NoError(t, err)
|
|
|
|
deps := newConformanceDeps()
|
|
server, err := NewServer(newConformanceConfig(), Dependencies{
|
|
Logger: nil,
|
|
Telemetry: nil,
|
|
Readiness: nil,
|
|
RuntimeRecords: deps.runtimeRecords,
|
|
RegisterRuntime: deps.registerRuntime,
|
|
ForceNextTurn: deps.forceNextTurn,
|
|
StopRuntime: deps.stopRuntime,
|
|
PatchRuntime: deps.patchRuntime,
|
|
BanishRace: deps.banishRace,
|
|
InvalidateMemberships: deps.membership,
|
|
GameLiveness: deps.liveness,
|
|
EngineVersions: deps.engineVersions,
|
|
CommandExecute: deps.commandExecute,
|
|
PutOrders: deps.putOrders,
|
|
GetReport: deps.getReport,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
cases := []conformanceCase{
|
|
{name: "internalHealthz", method: http.MethodGet, path: "/healthz"},
|
|
{name: "internalReadyz", method: http.MethodGet, path: "/readyz"},
|
|
{
|
|
name: "internalRegisterRuntime",
|
|
method: http.MethodPost,
|
|
path: "/api/v1/internal/games/" + conformanceGameID + "/register-runtime",
|
|
contentType: "application/json",
|
|
body: `{
|
|
"engine_endpoint": "http://galaxy-game-` + conformanceGameID + `:8080",
|
|
"members": [{"user_id": "user-1", "race_name": "Aelinari"}],
|
|
"target_engine_version": "1.2.3",
|
|
"turn_schedule": "0 18 * * *"
|
|
}`,
|
|
},
|
|
{
|
|
name: "internalBanishRace",
|
|
method: http.MethodPost,
|
|
path: "/api/v1/internal/games/" + conformanceGameID + "/race/Aelinari/banish",
|
|
expectedStatus: http.StatusNoContent,
|
|
},
|
|
{
|
|
name: "internalInvalidateMemberships",
|
|
method: http.MethodPost,
|
|
path: "/api/v1/internal/games/" + conformanceGameID + "/memberships/invalidate",
|
|
expectedStatus: http.StatusNoContent,
|
|
},
|
|
{
|
|
name: "internalGameLiveness",
|
|
method: http.MethodGet,
|
|
path: "/api/v1/internal/games/" + conformanceGameID + "/liveness",
|
|
},
|
|
{name: "internalListRuntimes", method: http.MethodGet, path: "/api/v1/internal/runtimes"},
|
|
{
|
|
name: "internalGetRuntime",
|
|
method: http.MethodGet,
|
|
path: "/api/v1/internal/runtimes/" + conformanceGameID,
|
|
},
|
|
{
|
|
name: "internalForceNextTurn",
|
|
method: http.MethodPost,
|
|
path: "/api/v1/internal/runtimes/" + conformanceGameID + "/force-next-turn",
|
|
},
|
|
{
|
|
name: "internalStopRuntime",
|
|
method: http.MethodPost,
|
|
path: "/api/v1/internal/runtimes/" + conformanceGameID + "/stop",
|
|
contentType: "application/json",
|
|
body: `{"reason":"admin_request"}`,
|
|
},
|
|
{
|
|
name: "internalPatchRuntime",
|
|
method: http.MethodPost,
|
|
path: "/api/v1/internal/runtimes/" + conformanceGameID + "/patch",
|
|
contentType: "application/json",
|
|
body: `{"version":"1.2.4"}`,
|
|
},
|
|
{name: "internalListEngineVersions", method: http.MethodGet, path: "/api/v1/internal/engine-versions"},
|
|
{
|
|
name: "internalCreateEngineVersion",
|
|
method: http.MethodPost,
|
|
path: "/api/v1/internal/engine-versions",
|
|
contentType: "application/json",
|
|
body: `{"version":"1.2.5","image_ref":"galaxy/game:1.2.5"}`,
|
|
expectedStatus: http.StatusCreated,
|
|
},
|
|
{
|
|
name: "internalGetEngineVersion",
|
|
method: http.MethodGet,
|
|
path: "/api/v1/internal/engine-versions/1.2.3",
|
|
},
|
|
{
|
|
name: "internalUpdateEngineVersion",
|
|
method: http.MethodPatch,
|
|
path: "/api/v1/internal/engine-versions/1.2.3",
|
|
contentType: "application/json",
|
|
body: `{"image_ref":"galaxy/game:1.2.3-patch"}`,
|
|
},
|
|
{
|
|
name: "internalDeprecateEngineVersion",
|
|
method: http.MethodDelete,
|
|
path: "/api/v1/internal/engine-versions/1.2.3",
|
|
expectedStatus: http.StatusNoContent,
|
|
},
|
|
{
|
|
name: "internalResolveEngineVersionImageRef",
|
|
method: http.MethodGet,
|
|
path: "/api/v1/internal/engine-versions/1.2.3/image-ref",
|
|
},
|
|
{
|
|
name: "internalExecuteCommands",
|
|
method: http.MethodPost,
|
|
path: "/api/v1/internal/games/" + conformanceGameID + "/commands",
|
|
contentType: "application/json",
|
|
body: `{"commands":[{"name":"build","args":{}}]}`,
|
|
extraHeaders: map[string]string{userIDHeader: conformanceUserID},
|
|
},
|
|
{
|
|
name: "internalPutOrders",
|
|
method: http.MethodPost,
|
|
path: "/api/v1/internal/games/" + conformanceGameID + "/orders",
|
|
contentType: "application/json",
|
|
body: `{"commands":[{"name":"move","args":{}}]}`,
|
|
extraHeaders: map[string]string{userIDHeader: conformanceUserID},
|
|
},
|
|
{
|
|
name: "internalGetReport",
|
|
method: http.MethodGet,
|
|
path: "/api/v1/internal/games/" + conformanceGameID + "/reports/0",
|
|
extraHeaders: map[string]string{userIDHeader: conformanceUserID},
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
runConformanceCase(t, server.handler, router, tc)
|
|
})
|
|
}
|
|
}
|
|
|
|
const (
|
|
conformanceGameID = "game-conformance"
|
|
conformanceUserID = "user-conformance"
|
|
conformanceServerURL = "http://localhost:8097"
|
|
userIDHeader = "X-User-ID"
|
|
)
|
|
|
|
type conformanceCase struct {
|
|
name string
|
|
method string
|
|
path string
|
|
contentType string
|
|
body string
|
|
expectedStatus int
|
|
extraHeaders map[string]string
|
|
}
|
|
|
|
func runConformanceCase(t *testing.T, handler http.Handler, router routers.Router, tc conformanceCase) {
|
|
t.Helper()
|
|
|
|
expectedStatus := tc.expectedStatus
|
|
if expectedStatus == 0 {
|
|
expectedStatus = http.StatusOK
|
|
}
|
|
|
|
var bodyReader io.Reader
|
|
if tc.body != "" {
|
|
bodyReader = strings.NewReader(tc.body)
|
|
}
|
|
request := httptest.NewRequest(tc.method, tc.path, bodyReader)
|
|
if tc.contentType != "" {
|
|
request.Header.Set("Content-Type", tc.contentType)
|
|
}
|
|
request.Header.Set("X-Galaxy-Caller", "admin")
|
|
for key, value := range tc.extraHeaders {
|
|
request.Header.Set(key, value)
|
|
}
|
|
|
|
recorder := httptest.NewRecorder()
|
|
handler.ServeHTTP(recorder, request)
|
|
require.Equalf(t, expectedStatus, recorder.Code,
|
|
"operation %s returned %d: %s", tc.name, recorder.Code, recorder.Body.String())
|
|
|
|
validationURL := conformanceServerURL + tc.path
|
|
validationRequest := httptest.NewRequest(tc.method, validationURL, bodyReaderFor(tc.body))
|
|
if tc.contentType != "" {
|
|
validationRequest.Header.Set("Content-Type", tc.contentType)
|
|
}
|
|
validationRequest.Header.Set("X-Galaxy-Caller", "admin")
|
|
for key, value := range tc.extraHeaders {
|
|
validationRequest.Header.Set(key, value)
|
|
}
|
|
|
|
route, pathParams, err := router.FindRoute(validationRequest)
|
|
require.NoError(t, err)
|
|
|
|
requestInput := &openapi3filter.RequestValidationInput{
|
|
Request: validationRequest,
|
|
PathParams: pathParams,
|
|
Route: route,
|
|
Options: &openapi3filter.Options{
|
|
IncludeResponseStatus: true,
|
|
},
|
|
}
|
|
require.NoError(t, openapi3filter.ValidateRequest(context.Background(), requestInput))
|
|
|
|
responseInput := &openapi3filter.ResponseValidationInput{
|
|
RequestValidationInput: requestInput,
|
|
Status: recorder.Code,
|
|
Header: recorder.Header(),
|
|
Options: &openapi3filter.Options{
|
|
IncludeResponseStatus: true,
|
|
},
|
|
}
|
|
responseInput.SetBodyBytes(recorder.Body.Bytes())
|
|
require.NoError(t, openapi3filter.ValidateResponse(context.Background(), responseInput))
|
|
}
|
|
|
|
func loadConformanceSpec(t *testing.T) *openapi3.T {
|
|
t.Helper()
|
|
|
|
_, thisFile, _, ok := runtime.Caller(0)
|
|
require.True(t, ok)
|
|
|
|
specPath := filepath.Join(filepath.Dir(thisFile), "..", "..", "..", "api", "internal-openapi.yaml")
|
|
loader := openapi3.NewLoader()
|
|
doc, err := loader.LoadFromFile(specPath)
|
|
require.NoError(t, err)
|
|
require.NoError(t, doc.Validate(context.Background()))
|
|
return doc
|
|
}
|
|
|
|
func bodyReaderFor(raw string) io.Reader {
|
|
if raw == "" {
|
|
return http.NoBody
|
|
}
|
|
return bytes.NewBufferString(raw)
|
|
}
|
|
|
|
func newConformanceConfig() Config {
|
|
return Config{
|
|
Addr: ":0",
|
|
ReadHeaderTimeout: time.Second,
|
|
ReadTimeout: time.Second,
|
|
WriteTimeout: time.Second,
|
|
IdleTimeout: time.Second,
|
|
}
|
|
}
|
|
|
|
// conformanceDeps groups the stub collaborators handed to the listener.
|
|
type conformanceDeps struct {
|
|
runtimeRecords *conformanceRuntimeRecords
|
|
registerRuntime *conformanceRegister
|
|
forceNextTurn *conformanceForce
|
|
stopRuntime *conformanceStop
|
|
patchRuntime *conformancePatch
|
|
banishRace *conformanceBanish
|
|
membership *conformanceMembership
|
|
liveness *conformanceLiveness
|
|
engineVersions *conformanceEngineVersions
|
|
commandExecute *conformanceCommands
|
|
putOrders *conformanceOrders
|
|
getReport *conformanceReport
|
|
}
|
|
|
|
func newConformanceDeps() *conformanceDeps {
|
|
return &conformanceDeps{
|
|
runtimeRecords: newConformanceRuntimeRecords(),
|
|
registerRuntime: &conformanceRegister{},
|
|
forceNextTurn: &conformanceForce{},
|
|
stopRuntime: &conformanceStop{},
|
|
patchRuntime: &conformancePatch{},
|
|
banishRace: &conformanceBanish{},
|
|
membership: &conformanceMembership{},
|
|
liveness: &conformanceLiveness{},
|
|
engineVersions: newConformanceEngineVersions(),
|
|
commandExecute: &conformanceCommands{},
|
|
putOrders: &conformanceOrders{},
|
|
getReport: &conformanceReport{},
|
|
}
|
|
}
|
|
|
|
// conformanceRecord builds a canonical running runtime record used
|
|
// by every stub service.
|
|
func conformanceRuntimeRecord() domainruntime.RuntimeRecord {
|
|
moment := time.Date(2026, 4, 30, 12, 0, 0, 0, time.UTC)
|
|
next := moment.Add(time.Minute)
|
|
started := moment
|
|
return domainruntime.RuntimeRecord{
|
|
GameID: conformanceGameID,
|
|
Status: domainruntime.StatusRunning,
|
|
EngineEndpoint: "http://galaxy-game-" + conformanceGameID + ":8080",
|
|
CurrentImageRef: "galaxy/game:1.2.3",
|
|
CurrentEngineVersion: "1.2.3",
|
|
TurnSchedule: "0 18 * * *",
|
|
CurrentTurn: 0,
|
|
NextGenerationAt: &next,
|
|
SkipNextTick: false,
|
|
EngineHealth: "healthy",
|
|
CreatedAt: moment,
|
|
UpdatedAt: moment,
|
|
StartedAt: &started,
|
|
}
|
|
}
|
|
|
|
func conformanceEngineVersionRecord(version string) engineversion.EngineVersion {
|
|
moment := time.Date(2026, 4, 30, 12, 0, 0, 0, time.UTC)
|
|
return engineversion.EngineVersion{
|
|
Version: version,
|
|
ImageRef: "galaxy/game:" + version,
|
|
Options: nil,
|
|
Status: engineversion.StatusActive,
|
|
CreatedAt: moment,
|
|
UpdatedAt: moment,
|
|
}
|
|
}
|
|
|
|
// conformanceRuntimeRecords is an in-memory store seeded with the
|
|
// canonical record so the get/list endpoints have something to return.
|
|
type conformanceRuntimeRecords struct {
|
|
mu sync.Mutex
|
|
stored map[string]domainruntime.RuntimeRecord
|
|
}
|
|
|
|
func newConformanceRuntimeRecords() *conformanceRuntimeRecords {
|
|
return &conformanceRuntimeRecords{
|
|
stored: map[string]domainruntime.RuntimeRecord{
|
|
conformanceGameID: conformanceRuntimeRecord(),
|
|
},
|
|
}
|
|
}
|
|
|
|
func (s *conformanceRuntimeRecords) Get(_ context.Context, gameID string) (domainruntime.RuntimeRecord, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
record, ok := s.stored[gameID]
|
|
if !ok {
|
|
return domainruntime.RuntimeRecord{}, domainruntime.ErrNotFound
|
|
}
|
|
return record, nil
|
|
}
|
|
|
|
func (s *conformanceRuntimeRecords) List(_ context.Context) ([]domainruntime.RuntimeRecord, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
out := make([]domainruntime.RuntimeRecord, 0, len(s.stored))
|
|
for _, record := range s.stored {
|
|
out = append(out, record)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (s *conformanceRuntimeRecords) ListByStatus(_ context.Context, status domainruntime.Status) ([]domainruntime.RuntimeRecord, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
out := make([]domainruntime.RuntimeRecord, 0, len(s.stored))
|
|
for _, record := range s.stored {
|
|
if record.Status == status {
|
|
out = append(out, record)
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
type conformanceRegister struct{}
|
|
|
|
func (s *conformanceRegister) Handle(_ context.Context, _ registerruntime.Input) (registerruntime.Result, error) {
|
|
return registerruntime.Result{
|
|
Record: conformanceRuntimeRecord(),
|
|
Outcome: operation.OutcomeSuccess,
|
|
}, nil
|
|
}
|
|
|
|
type conformanceForce struct{}
|
|
|
|
func (s *conformanceForce) Handle(_ context.Context, _ adminforce.Input) (adminforce.Result, error) {
|
|
return adminforce.Result{
|
|
TurnGeneration: turngeneration.Result{Record: conformanceRuntimeRecord()},
|
|
SkipScheduled: true,
|
|
Outcome: operation.OutcomeSuccess,
|
|
}, nil
|
|
}
|
|
|
|
type conformanceStop struct{}
|
|
|
|
func (s *conformanceStop) Handle(_ context.Context, _ adminstop.Input) (adminstop.Result, error) {
|
|
rec := conformanceRuntimeRecord()
|
|
rec.Status = domainruntime.StatusStopped
|
|
stopped := rec.UpdatedAt.Add(time.Second)
|
|
rec.StoppedAt = &stopped
|
|
rec.UpdatedAt = stopped
|
|
return adminstop.Result{Record: rec, Outcome: operation.OutcomeSuccess}, nil
|
|
}
|
|
|
|
type conformancePatch struct{}
|
|
|
|
func (s *conformancePatch) Handle(_ context.Context, in adminpatch.Input) (adminpatch.Result, error) {
|
|
rec := conformanceRuntimeRecord()
|
|
if in.Version != "" {
|
|
rec.CurrentImageRef = "galaxy/game:" + in.Version
|
|
rec.CurrentEngineVersion = in.Version
|
|
}
|
|
return adminpatch.Result{Record: rec, Outcome: operation.OutcomeSuccess}, nil
|
|
}
|
|
|
|
type conformanceBanish struct{}
|
|
|
|
func (s *conformanceBanish) Handle(_ context.Context, _ adminbanish.Input) (adminbanish.Result, error) {
|
|
return adminbanish.Result{Outcome: operation.OutcomeSuccess}, nil
|
|
}
|
|
|
|
type conformanceMembership struct{}
|
|
|
|
func (m *conformanceMembership) Invalidate(string) {}
|
|
|
|
type conformanceLiveness struct{}
|
|
|
|
func (s *conformanceLiveness) Handle(_ context.Context, _ livenessreply.Input) (livenessreply.Result, error) {
|
|
return livenessreply.Result{
|
|
Ready: true,
|
|
Status: domainruntime.StatusRunning,
|
|
}, nil
|
|
}
|
|
|
|
type conformanceEngineVersions struct {
|
|
mu sync.Mutex
|
|
versions map[string]engineversion.EngineVersion
|
|
}
|
|
|
|
func newConformanceEngineVersions() *conformanceEngineVersions {
|
|
return &conformanceEngineVersions{
|
|
versions: map[string]engineversion.EngineVersion{
|
|
"1.2.3": conformanceEngineVersionRecord("1.2.3"),
|
|
},
|
|
}
|
|
}
|
|
|
|
func (s *conformanceEngineVersions) List(_ context.Context, _ *engineversion.Status) ([]engineversion.EngineVersion, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
out := make([]engineversion.EngineVersion, 0, len(s.versions))
|
|
for _, version := range s.versions {
|
|
out = append(out, version)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (s *conformanceEngineVersions) Get(_ context.Context, version string) (engineversion.EngineVersion, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
v, ok := s.versions[version]
|
|
if !ok {
|
|
return engineversion.EngineVersion{}, engineversionsvc.ErrNotFound
|
|
}
|
|
return v, nil
|
|
}
|
|
|
|
func (s *conformanceEngineVersions) ResolveImageRef(_ context.Context, version string) (string, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
v, ok := s.versions[version]
|
|
if !ok {
|
|
return "", engineversionsvc.ErrNotFound
|
|
}
|
|
return v.ImageRef, nil
|
|
}
|
|
|
|
func (s *conformanceEngineVersions) Create(_ context.Context, in engineversionsvc.CreateInput) (engineversion.EngineVersion, error) {
|
|
rec := engineversion.EngineVersion{
|
|
Version: in.Version,
|
|
ImageRef: in.ImageRef,
|
|
Options: in.Options,
|
|
Status: engineversion.StatusActive,
|
|
CreatedAt: time.Date(2026, 4, 30, 12, 0, 0, 0, time.UTC),
|
|
UpdatedAt: time.Date(2026, 4, 30, 12, 0, 0, 0, time.UTC),
|
|
}
|
|
s.mu.Lock()
|
|
s.versions[in.Version] = rec
|
|
s.mu.Unlock()
|
|
return rec, nil
|
|
}
|
|
|
|
func (s *conformanceEngineVersions) Update(_ context.Context, in engineversionsvc.UpdateInput) (engineversion.EngineVersion, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
rec, ok := s.versions[in.Version]
|
|
if !ok {
|
|
return engineversion.EngineVersion{}, engineversionsvc.ErrNotFound
|
|
}
|
|
if in.ImageRef != nil {
|
|
rec.ImageRef = *in.ImageRef
|
|
}
|
|
if in.Status != nil {
|
|
rec.Status = *in.Status
|
|
}
|
|
rec.UpdatedAt = time.Date(2026, 4, 30, 13, 0, 0, 0, time.UTC)
|
|
s.versions[in.Version] = rec
|
|
return rec, nil
|
|
}
|
|
|
|
func (s *conformanceEngineVersions) Deprecate(_ context.Context, in engineversionsvc.DeprecateInput) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
rec, ok := s.versions[in.Version]
|
|
if !ok {
|
|
return engineversionsvc.ErrNotFound
|
|
}
|
|
rec.Status = engineversion.StatusDeprecated
|
|
rec.UpdatedAt = time.Date(2026, 4, 30, 14, 0, 0, 0, time.UTC)
|
|
s.versions[in.Version] = rec
|
|
return nil
|
|
}
|
|
|
|
type conformanceCommands struct{}
|
|
|
|
func (s *conformanceCommands) Handle(_ context.Context, _ commandexecute.Input) (commandexecute.Result, error) {
|
|
return commandexecute.Result{
|
|
Outcome: operation.OutcomeSuccess,
|
|
RawResponse: json.RawMessage(`{"results":[]}`),
|
|
}, nil
|
|
}
|
|
|
|
type conformanceOrders struct{}
|
|
|
|
func (s *conformanceOrders) Handle(_ context.Context, _ orderput.Input) (orderput.Result, error) {
|
|
return orderput.Result{
|
|
Outcome: operation.OutcomeSuccess,
|
|
RawResponse: json.RawMessage(`{"results":[]}`),
|
|
}, nil
|
|
}
|
|
|
|
type conformanceReport struct{}
|
|
|
|
func (s *conformanceReport) Handle(_ context.Context, _ reportget.Input) (reportget.Result, error) {
|
|
return reportget.Result{
|
|
Outcome: operation.OutcomeSuccess,
|
|
RawResponse: json.RawMessage(`{"player":"Aelinari","turn":0}`),
|
|
}, nil
|
|
}
|
|
|
|
// Compile-time guards that the stubs satisfy the handler-level
|
|
// service interfaces accepted by the listener.
|
|
var (
|
|
_ handlers.RegisterRuntimeService = (*conformanceRegister)(nil)
|
|
_ handlers.ForceNextTurnService = (*conformanceForce)(nil)
|
|
_ handlers.StopRuntimeService = (*conformanceStop)(nil)
|
|
_ handlers.PatchRuntimeService = (*conformancePatch)(nil)
|
|
_ handlers.BanishRaceService = (*conformanceBanish)(nil)
|
|
_ handlers.MembershipInvalidator = (*conformanceMembership)(nil)
|
|
_ handlers.LivenessService = (*conformanceLiveness)(nil)
|
|
_ handlers.EngineVersionService = (*conformanceEngineVersions)(nil)
|
|
_ handlers.CommandExecuteService = (*conformanceCommands)(nil)
|
|
_ handlers.OrderPutService = (*conformanceOrders)(nil)
|
|
_ handlers.ReportGetService = (*conformanceReport)(nil)
|
|
_ handlers.RuntimeRecordsReader = (*conformanceRuntimeRecords)(nil)
|
|
)
|