package server import ( "encoding/json" "net/http" "net/http/httptest" "strings" "testing" ) // TestOAuthProviderNotConfigured — ручка входа через провайдера отвечает // понятной ошибкой, пока оператор не задал OAUTH_* (Фаза 7). func TestOAuthProviderNotConfigured(t *testing.T) { srv, _ := newTestServer(t) rec := doJSON(t, srv, http.MethodGet, "/api/v1/auth/oauth/github/start", "") if rec.Code != http.StatusNotFound { t.Fatalf("oauth start without config = %d, want 404, body = %s", rec.Code, rec.Body.String()) } if code := errorCodeOf(t, rec); code != "oauth.provider_not_configured" { t.Fatalf("error code = %q, want oauth.provider_not_configured", code) } rec = doJSON(t, srv, http.MethodGet, "/api/v1/auth/oauth/vk/start", "") if rec.Code != http.StatusNotFound { t.Fatalf("unknown provider = %d, want 404", rec.Code) } if code := errorCodeOf(t, rec); code != "oauth.provider_unknown" { t.Fatalf("error code = %q, want oauth.provider_unknown", code) } // Список провайдеров пуст, клиент не рисует кнопки. rec = doJSON(t, srv, http.MethodGet, "/api/v1/auth/oauth/providers", "") if rec.Code != http.StatusOK { t.Fatalf("providers list = %d, body = %s", rec.Code, rec.Body.String()) } var payload struct { Providers []struct { ID string `json:"id"` } `json:"providers"` RedirectURL string `json:"redirect_url"` } if err := json.Unmarshal(rec.Body.Bytes(), &payload); err != nil { t.Fatalf("decode providers: %v", err) } if len(payload.Providers) != 0 { t.Fatalf("providers = %s, want empty", rec.Body.String()) } // Мета инстанса честно сообщает, что провайдеры выключены. meta := doJSON(t, srv, http.MethodGet, "/api/v1/meta", "") if meta.Code != http.StatusOK { t.Fatalf("meta = %d", meta.Code) } if !strings.Contains(meta.Body.String(), `"oauth_enabled":false`) { t.Fatalf("meta must report oauth_enabled=false: %s", meta.Body.String()) } if !strings.Contains(meta.Body.String(), `"passkeys_enabled":true`) { t.Fatalf("meta must report passkeys_enabled=true for a domain: %s", meta.Body.String()) } } // TestOAuthCallbackRejectsUnsignedState — колбэк без валидного state // возвращает пользователя на вход с кодом ошибки, а не создаёт сессию. func TestOAuthCallbackRejectsUnsignedState(t *testing.T) { srv, _ := newTestServer(t) request := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/api/v1/auth/oauth/github/callback?code=abc&state=forged", nil) rec := httptest.NewRecorder() srv.Handler().ServeHTTP(rec, request) if rec.Code != http.StatusFound { t.Fatalf("callback with forged state = %d, want 302, body = %s", rec.Code, rec.Body.String()) } location := rec.Header().Get("Location") if !strings.HasPrefix(location, "/login?oauth_error=") { t.Fatalf("callback must redirect to the login page with a code, got %q", location) } if len(rec.Result().Cookies()) != 0 { t.Fatal("failed oauth callback must not set a session cookie") } }