234 lines
10 KiB
Go
234 lines
10 KiB
Go
package internalhttp
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"galaxy/user/internal/service/accountview"
|
|
"galaxy/user/internal/service/adminusers"
|
|
"galaxy/user/internal/service/shared"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestAdminReadHandlersSuccessCases(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
handler := mustNewHandler(t, Dependencies{
|
|
GetUserByID: getUserByIDFunc(func(_ context.Context, input adminusers.GetUserByIDInput) (adminusers.LookupResult, error) {
|
|
require.Equal(t, "user-123", input.UserID)
|
|
return adminusers.LookupResult{User: sampleAccountView()}, nil
|
|
}),
|
|
GetUserByEmail: getUserByEmailFunc(func(_ context.Context, input adminusers.GetUserByEmailInput) (adminusers.LookupResult, error) {
|
|
require.Equal(t, "pilot@example.com", input.Email)
|
|
return adminusers.LookupResult{User: sampleAccountView()}, nil
|
|
}),
|
|
GetUserByUserName: getUserByUserNameFunc(func(_ context.Context, input adminusers.GetUserByUserNameInput) (adminusers.LookupResult, error) {
|
|
require.Equal(t, "player-abcdefgh", input.UserName)
|
|
return adminusers.LookupResult{User: sampleAccountView()}, nil
|
|
}),
|
|
ListUsers: listUsersFunc(func(_ context.Context, input adminusers.ListUsersInput) (adminusers.ListUsersResult, error) {
|
|
require.Equal(t, 2, input.PageSize)
|
|
require.Equal(t, "cursor-1", input.PageToken)
|
|
require.Equal(t, "paid", input.PaidState)
|
|
require.Equal(t, "DE", input.DeclaredCountry)
|
|
require.Equal(t, "login_block", input.SanctionCode)
|
|
require.Equal(t, "max_owned_private_games", input.LimitCode)
|
|
require.NotNil(t, input.PaidExpiresBefore)
|
|
require.NotNil(t, input.PaidExpiresAfter)
|
|
require.NotNil(t, input.CanLogin)
|
|
require.NotNil(t, input.CanCreatePrivateGame)
|
|
require.NotNil(t, input.CanJoinGame)
|
|
require.False(t, *input.CanLogin)
|
|
require.True(t, *input.CanCreatePrivateGame)
|
|
require.True(t, *input.CanJoinGame)
|
|
require.Equal(t, time.Date(2026, time.April, 10, 12, 0, 0, 0, time.UTC), input.PaidExpiresBefore.UTC())
|
|
require.Equal(t, time.Date(2026, time.April, 1, 12, 0, 0, 0, time.UTC), input.PaidExpiresAfter.UTC())
|
|
|
|
other := sampleAccountView()
|
|
other.UserID = "user-234"
|
|
other.Email = "second@example.com"
|
|
other.UserName = "player-second12"
|
|
|
|
return adminusers.ListUsersResult{
|
|
Items: []accountview.AccountView{sampleAccountView(), other},
|
|
NextPageToken: "cursor-2",
|
|
}, nil
|
|
}),
|
|
})
|
|
|
|
tests := []struct {
|
|
name string
|
|
method string
|
|
path string
|
|
body string
|
|
wantStatus int
|
|
wantBody string
|
|
}{
|
|
{
|
|
name: "get user by id",
|
|
method: http.MethodGet,
|
|
path: "/api/v1/internal/users/user-123",
|
|
wantStatus: http.StatusOK,
|
|
wantBody: `{"user":{"user_id":"user-123","email":"pilot@example.com","user_name":"player-abcdefgh","preferred_language":"en","time_zone":"Europe/Kaliningrad","declared_country":"DE","entitlement":{"plan_code":"free","is_paid":false,"source":"auth_registration","actor":{"type":"service","id":"user-service"},"reason_code":"initial_free_entitlement","starts_at":"2026-04-09T10:00:00Z","updated_at":"2026-04-09T10:00:00Z"},"active_sanctions":[],"active_limits":[],"created_at":"2026-04-09T10:00:00Z","updated_at":"2026-04-09T10:00:00Z"}}`,
|
|
},
|
|
{
|
|
name: "get user by email",
|
|
method: http.MethodPost,
|
|
path: "/api/v1/internal/user-lookups/by-email",
|
|
body: `{"email":"pilot@example.com"}`,
|
|
wantStatus: http.StatusOK,
|
|
wantBody: `{"user":{"user_id":"user-123","email":"pilot@example.com","user_name":"player-abcdefgh","preferred_language":"en","time_zone":"Europe/Kaliningrad","declared_country":"DE","entitlement":{"plan_code":"free","is_paid":false,"source":"auth_registration","actor":{"type":"service","id":"user-service"},"reason_code":"initial_free_entitlement","starts_at":"2026-04-09T10:00:00Z","updated_at":"2026-04-09T10:00:00Z"},"active_sanctions":[],"active_limits":[],"created_at":"2026-04-09T10:00:00Z","updated_at":"2026-04-09T10:00:00Z"}}`,
|
|
},
|
|
{
|
|
name: "get user by user name",
|
|
method: http.MethodPost,
|
|
path: "/api/v1/internal/user-lookups/by-user-name",
|
|
body: `{"user_name":"player-abcdefgh"}`,
|
|
wantStatus: http.StatusOK,
|
|
wantBody: `{"user":{"user_id":"user-123","email":"pilot@example.com","user_name":"player-abcdefgh","preferred_language":"en","time_zone":"Europe/Kaliningrad","declared_country":"DE","entitlement":{"plan_code":"free","is_paid":false,"source":"auth_registration","actor":{"type":"service","id":"user-service"},"reason_code":"initial_free_entitlement","starts_at":"2026-04-09T10:00:00Z","updated_at":"2026-04-09T10:00:00Z"},"active_sanctions":[],"active_limits":[],"created_at":"2026-04-09T10:00:00Z","updated_at":"2026-04-09T10:00:00Z"}}`,
|
|
},
|
|
{
|
|
name: "list users",
|
|
method: http.MethodGet,
|
|
path: "/api/v1/internal/users?page_size=2&page_token=cursor-1&paid_state=paid&paid_expires_before=2026-04-10T12:00:00Z&paid_expires_after=2026-04-01T12:00:00Z&declared_country=DE&sanction_code=login_block&limit_code=max_owned_private_games&can_login=false&can_create_private_game=true&can_join_game=true",
|
|
wantStatus: http.StatusOK,
|
|
wantBody: `{"items":[{"user_id":"user-123","email":"pilot@example.com","user_name":"player-abcdefgh","preferred_language":"en","time_zone":"Europe/Kaliningrad","declared_country":"DE","entitlement":{"plan_code":"free","is_paid":false,"source":"auth_registration","actor":{"type":"service","id":"user-service"},"reason_code":"initial_free_entitlement","starts_at":"2026-04-09T10:00:00Z","updated_at":"2026-04-09T10:00:00Z"},"active_sanctions":[],"active_limits":[],"created_at":"2026-04-09T10:00:00Z","updated_at":"2026-04-09T10:00:00Z"},{"user_id":"user-234","email":"second@example.com","user_name":"player-second12","preferred_language":"en","time_zone":"Europe/Kaliningrad","declared_country":"DE","entitlement":{"plan_code":"free","is_paid":false,"source":"auth_registration","actor":{"type":"service","id":"user-service"},"reason_code":"initial_free_entitlement","starts_at":"2026-04-09T10:00:00Z","updated_at":"2026-04-09T10:00:00Z"},"active_sanctions":[],"active_limits":[],"created_at":"2026-04-09T10:00:00Z","updated_at":"2026-04-09T10:00:00Z"}],"next_page_token":"cursor-2"}`,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var body *bytes.Buffer
|
|
if tt.body != "" {
|
|
body = bytes.NewBufferString(tt.body)
|
|
} else {
|
|
body = &bytes.Buffer{}
|
|
}
|
|
|
|
request := httptest.NewRequest(tt.method, tt.path, body)
|
|
if tt.body != "" {
|
|
request.Header.Set("Content-Type", "application/json")
|
|
}
|
|
recorder := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(recorder, request)
|
|
|
|
require.Equal(t, tt.wantStatus, recorder.Code)
|
|
assertJSONEq(t, recorder.Body.String(), tt.wantBody)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAdminReadHandlersErrorCases(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
handler := mustNewHandler(t, Dependencies{
|
|
GetUserByID: getUserByIDFunc(func(context.Context, adminusers.GetUserByIDInput) (adminusers.LookupResult, error) {
|
|
return adminusers.LookupResult{}, shared.SubjectNotFound()
|
|
}),
|
|
GetUserByEmail: getUserByEmailFunc(func(context.Context, adminusers.GetUserByEmailInput) (adminusers.LookupResult, error) {
|
|
return adminusers.LookupResult{}, shared.SubjectNotFound()
|
|
}),
|
|
GetUserByUserName: getUserByUserNameFunc(func(context.Context, adminusers.GetUserByUserNameInput) (adminusers.LookupResult, error) {
|
|
return adminusers.LookupResult{}, shared.SubjectNotFound()
|
|
}),
|
|
ListUsers: listUsersFunc(func(context.Context, adminusers.ListUsersInput) (adminusers.ListUsersResult, error) {
|
|
return adminusers.ListUsersResult{}, shared.InvalidRequest("page_token is invalid or does not match current filters")
|
|
}),
|
|
})
|
|
|
|
tests := []struct {
|
|
name string
|
|
method string
|
|
path string
|
|
body string
|
|
wantStatus int
|
|
wantBody string
|
|
}{
|
|
{
|
|
name: "get user by id not found",
|
|
method: http.MethodGet,
|
|
path: "/api/v1/internal/users/user-missing",
|
|
wantStatus: http.StatusNotFound,
|
|
wantBody: `{"error":{"code":"subject_not_found","message":"subject not found"}}`,
|
|
},
|
|
{
|
|
name: "get user by email malformed json",
|
|
method: http.MethodPost,
|
|
path: "/api/v1/internal/user-lookups/by-email",
|
|
body: `{"email":"pilot@example.com","extra":true}`,
|
|
wantStatus: http.StatusBadRequest,
|
|
wantBody: `{"error":{"code":"invalid_request","message":"request body contains unknown field \"extra\""}}`,
|
|
},
|
|
{
|
|
name: "get user by user name not found",
|
|
method: http.MethodPost,
|
|
path: "/api/v1/internal/user-lookups/by-user-name",
|
|
body: `{"user_name":"player-missingx"}`,
|
|
wantStatus: http.StatusNotFound,
|
|
wantBody: `{"error":{"code":"subject_not_found","message":"subject not found"}}`,
|
|
},
|
|
{
|
|
name: "list users invalid page size",
|
|
method: http.MethodGet,
|
|
path: "/api/v1/internal/users?page_size=201",
|
|
wantStatus: http.StatusBadRequest,
|
|
wantBody: `{"error":{"code":"invalid_request","message":"page_size must be between 1 and 200"}}`,
|
|
},
|
|
{
|
|
name: "list users invalid timestamp",
|
|
method: http.MethodGet,
|
|
path: "/api/v1/internal/users?paid_expires_before=not-a-time",
|
|
wantStatus: http.StatusBadRequest,
|
|
wantBody: `{"error":{"code":"invalid_request","message":"paid_expires_before must be a valid RFC 3339 timestamp"}}`,
|
|
},
|
|
{
|
|
name: "list users invalid boolean",
|
|
method: http.MethodGet,
|
|
path: "/api/v1/internal/users?can_login=maybe",
|
|
wantStatus: http.StatusBadRequest,
|
|
wantBody: `{"error":{"code":"invalid_request","message":"can_login must be a valid boolean"}}`,
|
|
},
|
|
{
|
|
name: "list users invalid page token",
|
|
method: http.MethodGet,
|
|
path: "/api/v1/internal/users?page_token=cursor-1",
|
|
wantStatus: http.StatusBadRequest,
|
|
wantBody: `{"error":{"code":"invalid_request","message":"page_token is invalid or does not match current filters"}}`,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var body *bytes.Buffer
|
|
if tt.body != "" {
|
|
body = bytes.NewBufferString(tt.body)
|
|
} else {
|
|
body = &bytes.Buffer{}
|
|
}
|
|
|
|
request := httptest.NewRequest(tt.method, tt.path, body)
|
|
if tt.body != "" {
|
|
request.Header.Set("Content-Type", "application/json")
|
|
}
|
|
recorder := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(recorder, request)
|
|
|
|
require.Equal(t, tt.wantStatus, recorder.Code)
|
|
assertJSONEq(t, recorder.Body.String(), tt.wantBody)
|
|
})
|
|
}
|
|
}
|