191 lines
6.1 KiB
Go
191 lines
6.1 KiB
Go
package backendclient_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"galaxy/gateway/internal/backendclient"
|
|
"galaxy/gateway/internal/session"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func newRESTClient(t *testing.T, server *httptest.Server) *backendclient.RESTClient {
|
|
t.Helper()
|
|
cfg := backendclient.Config{
|
|
HTTPBaseURL: server.URL,
|
|
GRPCPushURL: "passthrough://test",
|
|
GatewayClientID: "test-gateway",
|
|
HTTPTimeout: time.Second,
|
|
PushReconnectBaseBackoff: 10 * time.Millisecond,
|
|
PushReconnectMaxBackoff: 100 * time.Millisecond,
|
|
}
|
|
client, err := backendclient.NewRESTClient(cfg)
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() { _ = client.Close() })
|
|
return client
|
|
}
|
|
|
|
func TestRESTClientLookupSessionReturnsActiveRecord(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
require.Equal(t, http.MethodGet, r.Method)
|
|
require.Equal(t, "/api/v1/internal/sessions/device-1", r.URL.Path)
|
|
writeJSON(t, w, http.StatusOK, map[string]any{
|
|
"device_session_id": "device-1",
|
|
"user_id": "user-1",
|
|
"status": "active",
|
|
"client_public_key": "pk-1",
|
|
"created_at": "2026-04-01T00:00:00Z",
|
|
})
|
|
}))
|
|
t.Cleanup(server.Close)
|
|
|
|
client := newRESTClient(t, server)
|
|
rec, err := client.LookupSession(context.Background(), "device-1")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, session.Record{
|
|
DeviceSessionID: "device-1",
|
|
UserID: "user-1",
|
|
ClientPublicKey: "pk-1",
|
|
Status: session.StatusActive,
|
|
}, rec)
|
|
}
|
|
|
|
func TestRESTClientLookupSessionReturnsRevokedRecord(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
writeJSON(t, w, http.StatusOK, map[string]any{
|
|
"device_session_id": "device-2",
|
|
"user_id": "user-2",
|
|
"status": "revoked",
|
|
"client_public_key": "pk-2",
|
|
"created_at": "2026-04-01T00:00:00Z",
|
|
"revoked_at": "2026-04-01T00:01:00Z",
|
|
})
|
|
}))
|
|
t.Cleanup(server.Close)
|
|
|
|
client := newRESTClient(t, server)
|
|
rec, err := client.LookupSession(context.Background(), "device-2")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, session.StatusRevoked, rec.Status)
|
|
require.NotNil(t, rec.RevokedAtMS)
|
|
assert.Equal(t, time.Date(2026, 4, 1, 0, 1, 0, 0, time.UTC).UnixMilli(), *rec.RevokedAtMS)
|
|
}
|
|
|
|
func TestRESTClientLookupSessionMapsNotFound(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
writeJSON(t, w, http.StatusNotFound, map[string]any{"error": map[string]any{"code": "subject_not_found", "message": "missing"}})
|
|
}))
|
|
t.Cleanup(server.Close)
|
|
|
|
client := newRESTClient(t, server)
|
|
_, err := client.LookupSession(context.Background(), "missing")
|
|
require.Error(t, err)
|
|
assert.True(t, errors.Is(err, session.ErrNotFound))
|
|
}
|
|
|
|
func TestRESTClientLookupSessionRejectsMismatchedID(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
writeJSON(t, w, http.StatusOK, map[string]any{
|
|
"device_session_id": "other",
|
|
"user_id": "user-1",
|
|
"status": "active",
|
|
"client_public_key": "pk-1",
|
|
"created_at": "2026-04-01T00:00:00Z",
|
|
})
|
|
}))
|
|
t.Cleanup(server.Close)
|
|
|
|
client := newRESTClient(t, server)
|
|
_, err := client.LookupSession(context.Background(), "device-1")
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "does not match requested")
|
|
}
|
|
|
|
func TestRESTClientSendEmailCodeForwardsAcceptLanguage(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
require.Equal(t, http.MethodPost, r.Method)
|
|
require.Equal(t, "/api/v1/public/auth/send-email-code", r.URL.Path)
|
|
require.Equal(t, "ru-RU", r.Header.Get("Accept-Language"))
|
|
writeJSON(t, w, http.StatusOK, map[string]any{"challenge_id": "challenge-1"})
|
|
}))
|
|
t.Cleanup(server.Close)
|
|
|
|
client := newRESTClient(t, server)
|
|
out, err := client.SendEmailCode(context.Background(), backendclient.SendEmailCodeInput{
|
|
Email: "user@example.com",
|
|
PreferredLanguage: "ru-RU",
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "challenge-1", out.ChallengeID)
|
|
}
|
|
|
|
func TestRESTClientSendEmailCodeProjectsAuthError(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
writeJSON(t, w, http.StatusBadRequest, map[string]any{
|
|
"error": map[string]any{"code": "invalid_request", "message": "bad email"},
|
|
})
|
|
}))
|
|
t.Cleanup(server.Close)
|
|
|
|
client := newRESTClient(t, server)
|
|
_, err := client.SendEmailCode(context.Background(), backendclient.SendEmailCodeInput{Email: "user@example.com"})
|
|
require.Error(t, err)
|
|
var authErr *backendclient.AuthError
|
|
require.ErrorAs(t, err, &authErr)
|
|
assert.Equal(t, http.StatusBadRequest, authErr.StatusCode)
|
|
assert.Equal(t, "invalid_request", authErr.Code)
|
|
assert.Equal(t, "bad email", authErr.Message)
|
|
}
|
|
|
|
func TestRESTClientConfirmEmailCodeReturnsDeviceSession(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
require.Equal(t, "/api/v1/public/auth/confirm-email-code", r.URL.Path)
|
|
|
|
var body backendclient.ConfirmEmailCodeInput
|
|
require.NoError(t, json.NewDecoder(r.Body).Decode(&body))
|
|
assert.Equal(t, "challenge-1", body.ChallengeID)
|
|
writeJSON(t, w, http.StatusOK, map[string]any{"device_session_id": "device-1"})
|
|
}))
|
|
t.Cleanup(server.Close)
|
|
|
|
client := newRESTClient(t, server)
|
|
out, err := client.ConfirmEmailCode(context.Background(), backendclient.ConfirmEmailCodeInput{
|
|
ChallengeID: "challenge-1",
|
|
Code: "12345",
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "device-1", out.DeviceSessionID)
|
|
}
|
|
|
|
func writeJSON(t *testing.T, w http.ResponseWriter, status int, body any) {
|
|
t.Helper()
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(status)
|
|
require.NoError(t, json.NewEncoder(w).Encode(body))
|
|
}
|
|
|
|
// guard ensures package keeps testify dependency.
|
|
var _ = strings.TrimSpace
|