Files
galaxy-game/gateway/internal/downstream/lobbyservice/client_test.go
T
2026-04-28 20:39:18 +02:00

213 lines
7.0 KiB
Go

package lobbyservice_test
import (
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"galaxy/gateway/internal/downstream"
"galaxy/gateway/internal/downstream/lobbyservice"
lobbymodel "galaxy/model/lobby"
"galaxy/transcoder"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestExecuteMyGamesListSuccess(t *testing.T) {
t.Parallel()
expectedResponse := lobbymodel.MyGamesListResponse{
Items: []lobbymodel.GameSummary{
{
GameID: "game-1",
GameName: "Nebula Clash",
GameType: "private",
Status: "draft",
OwnerUserID: "user-1",
MinPlayers: 2,
MaxPlayers: 8,
EnrollmentEndsAt: time.Date(2026, 5, 1, 12, 0, 0, 0, time.UTC),
CreatedAt: time.Date(2026, 4, 28, 9, 0, 0, 0, time.UTC),
UpdatedAt: time.Date(2026, 4, 28, 9, 5, 0, 0, time.UTC),
},
},
}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, http.MethodGet, r.Method)
assert.Equal(t, "/api/v1/lobby/my/games", r.URL.Path)
assert.Equal(t, "user-1", r.Header.Get("X-User-Id"))
w.Header().Set("Content-Type", "application/json")
require.NoError(t, json.NewEncoder(w).Encode(expectedResponse))
}))
t.Cleanup(server.Close)
client, err := lobbyservice.NewHTTPClient(server.URL)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, client.Close()) })
requestBytes, err := transcoder.MyGamesListRequestToPayload(&lobbymodel.MyGamesListRequest{})
require.NoError(t, err)
result, err := client.ExecuteCommand(context.Background(), downstream.AuthenticatedCommand{
MessageType: lobbymodel.MessageTypeMyGamesList,
UserID: "user-1",
PayloadBytes: requestBytes,
})
require.NoError(t, err)
assert.Equal(t, "ok", result.ResultCode)
decoded, err := transcoder.PayloadToMyGamesListResponse(result.PayloadBytes)
require.NoError(t, err)
require.Len(t, decoded.Items, 1)
assert.Equal(t, expectedResponse.Items[0].GameID, decoded.Items[0].GameID)
assert.Equal(t, expectedResponse.Items[0].OwnerUserID, decoded.Items[0].OwnerUserID)
assert.Equal(t, expectedResponse.Items[0].MinPlayers, decoded.Items[0].MinPlayers)
}
func TestExecuteOpenEnrollmentSuccess(t *testing.T) {
t.Parallel()
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, http.MethodPost, r.Method)
assert.Equal(t, "/api/v1/lobby/games/game-77/open-enrollment", r.URL.Path)
assert.Equal(t, "owner-1", r.Header.Get("X-User-Id"))
w.Header().Set("Content-Type", "application/json")
require.NoError(t, json.NewEncoder(w).Encode(map[string]any{
"game_id": "game-77",
"status": "enrollment_open",
}))
}))
t.Cleanup(server.Close)
client, err := lobbyservice.NewHTTPClient(server.URL)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, client.Close()) })
requestBytes, err := transcoder.OpenEnrollmentRequestToPayload(&lobbymodel.OpenEnrollmentRequest{GameID: "game-77"})
require.NoError(t, err)
result, err := client.ExecuteCommand(context.Background(), downstream.AuthenticatedCommand{
MessageType: lobbymodel.MessageTypeOpenEnrollment,
UserID: "owner-1",
PayloadBytes: requestBytes,
})
require.NoError(t, err)
assert.Equal(t, "ok", result.ResultCode)
decoded, err := transcoder.PayloadToOpenEnrollmentResponse(result.PayloadBytes)
require.NoError(t, err)
assert.Equal(t, "game-77", decoded.GameID)
assert.Equal(t, "enrollment_open", decoded.Status)
}
func TestExecuteOpenEnrollmentForbiddenProjectsErrorEnvelope(t *testing.T) {
t.Parallel()
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusForbidden)
require.NoError(t, json.NewEncoder(w).Encode(map[string]any{
"error": map[string]string{
"code": "forbidden",
"message": "only the game owner may open enrollment",
},
}))
}))
t.Cleanup(server.Close)
client, err := lobbyservice.NewHTTPClient(server.URL)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, client.Close()) })
requestBytes, err := transcoder.OpenEnrollmentRequestToPayload(&lobbymodel.OpenEnrollmentRequest{GameID: "game-77"})
require.NoError(t, err)
result, err := client.ExecuteCommand(context.Background(), downstream.AuthenticatedCommand{
MessageType: lobbymodel.MessageTypeOpenEnrollment,
UserID: "non-owner",
PayloadBytes: requestBytes,
})
require.NoError(t, err)
assert.Equal(t, "forbidden", result.ResultCode)
decoded, err := transcoder.PayloadToLobbyErrorResponse(result.PayloadBytes)
require.NoError(t, err)
assert.Equal(t, "forbidden", decoded.Error.Code)
assert.NotEmpty(t, decoded.Error.Message)
}
func TestExecuteCommandUnavailableProjectsErrUnavailable(t *testing.T) {
t.Parallel()
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusServiceUnavailable)
}))
t.Cleanup(server.Close)
client, err := lobbyservice.NewHTTPClient(server.URL)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, client.Close()) })
requestBytes, err := transcoder.MyGamesListRequestToPayload(&lobbymodel.MyGamesListRequest{})
require.NoError(t, err)
_, err = client.ExecuteCommand(context.Background(), downstream.AuthenticatedCommand{
MessageType: lobbymodel.MessageTypeMyGamesList,
UserID: "user-1",
PayloadBytes: requestBytes,
})
require.Error(t, err)
assert.True(t, errors.Is(err, downstream.ErrDownstreamUnavailable))
}
func TestExecuteCommandRejectsEmptyUserID(t *testing.T) {
t.Parallel()
client, err := lobbyservice.NewHTTPClient("http://127.0.0.1:1")
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, client.Close()) })
requestBytes, err := transcoder.MyGamesListRequestToPayload(&lobbymodel.MyGamesListRequest{})
require.NoError(t, err)
_, err = client.ExecuteCommand(context.Background(), downstream.AuthenticatedCommand{
MessageType: lobbymodel.MessageTypeMyGamesList,
UserID: "",
PayloadBytes: requestBytes,
})
require.Error(t, err)
assert.True(t, strings.Contains(err.Error(), "user_id"), "error must mention user_id; got %q", err.Error())
}
func TestNewRoutesReservesUnavailableClientWhenBaseURLEmpty(t *testing.T) {
t.Parallel()
routes, closeFn, err := lobbyservice.NewRoutes("")
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, closeFn()) })
require.Contains(t, routes, lobbymodel.MessageTypeMyGamesList)
require.Contains(t, routes, lobbymodel.MessageTypeOpenEnrollment)
requestBytes, err := transcoder.MyGamesListRequestToPayload(&lobbymodel.MyGamesListRequest{})
require.NoError(t, err)
_, err = routes[lobbymodel.MessageTypeMyGamesList].ExecuteCommand(
context.Background(),
downstream.AuthenticatedCommand{
MessageType: lobbymodel.MessageTypeMyGamesList,
UserID: "user-1",
PayloadBytes: requestBytes,
},
)
require.Error(t, err)
assert.True(t, errors.Is(err, downstream.ErrDownstreamUnavailable))
}