package backendclient_test import ( "context" "testing" "galaxy/gateway/internal/backendclient" "galaxy/gateway/internal/downstream" lobbymodel "galaxy/model/lobby" ordermodel "galaxy/model/order" reportmodel "galaxy/model/report" usermodel "galaxy/model/user" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // Phase 14 follow-up: every authenticated message-type constant // declared in `pkg/model/` must be wired into the matching // route table. Without this regression test, adding a new constant // without registering it surfaces only at runtime as // `unimplemented: message_type is not routed` — exactly what the // owner saw when an outdated gateway image missed // `user.games.order.get`. func TestRoutesCoverAllAuthenticatedMessageTypes(t *testing.T) { t.Parallel() cases := map[string]struct { expected []string actual map[string]downstream.Client }{ "user": { expected: []string{ usermodel.MessageTypeGetMyAccount, usermodel.MessageTypeUpdateMyProfile, usermodel.MessageTypeUpdateMySettings, usermodel.MessageTypeListMySessions, usermodel.MessageTypeRevokeMySession, usermodel.MessageTypeRevokeAllMySessions, }, actual: backendclient.UserRoutes(nil), }, "lobby": { expected: []string{ lobbymodel.MessageTypeMyGamesList, lobbymodel.MessageTypePublicGamesList, lobbymodel.MessageTypeMyApplicationsList, lobbymodel.MessageTypeMyInvitesList, lobbymodel.MessageTypeOpenEnrollment, lobbymodel.MessageTypeGameCreate, lobbymodel.MessageTypeApplicationSubmit, lobbymodel.MessageTypeInviteRedeem, lobbymodel.MessageTypeInviteDecline, }, actual: backendclient.LobbyRoutes(nil), }, "game": { expected: []string{ ordermodel.MessageTypeUserGamesCommand, ordermodel.MessageTypeUserGamesOrder, ordermodel.MessageTypeUserGamesOrderGet, reportmodel.MessageTypeUserGamesReport, }, actual: backendclient.GameRoutes(nil), }, } for name, tc := range cases { t.Run(name, func(t *testing.T) { t.Parallel() require.Len(t, tc.actual, len(tc.expected), "%s routes table size diverges from the expected message-type list", name) for _, mt := range tc.expected { client, ok := tc.actual[mt] assert.Truef(t, ok, "%s routes are missing %q", name, mt) assert.NotNilf(t, client, "%s routes resolve %q to a nil client", name, mt) } }) } } // Sanity-check that the order-get route really points at the game // command client (and not, say, the lobby one if a future refactor // reshuffles the helpers): the route table must dispatch through // `gameCommandClient.ExecuteCommand`, which in turn calls // `RESTClient.ExecuteGameCommand`. We exercise this through the // public Router contract. func TestUserGamesOrderGetRoutedToGameClient(t *testing.T) { t.Parallel() routes := backendclient.GameRoutes(nil) router := downstream.NewStaticRouter(routes) client, err := router.Route(ordermodel.MessageTypeUserGamesOrderGet) require.NoError(t, err) require.NotNil(t, client) // Without a live RESTClient the client is the unavailable stub — // calling ExecuteCommand surfaces the canonical "downstream // service is unavailable" sentinel rather than the "not routed" // error we want to keep regression-tested. _, err = client.ExecuteCommand(context.Background(), downstream.AuthenticatedCommand{ MessageType: ordermodel.MessageTypeUserGamesOrderGet, }) assert.ErrorIs(t, err, downstream.ErrDownstreamUnavailable) }