tests: update tests to use httpx.AsyncClient (#3984)

* Add async support and dependencies to pyproject.toml files

- Added `asgi-lifespan>=2.1.0` to dependencies.
- Configured `asyncio_mode` and `asyncio_default_fixture_loop_scope` for pytest.
- Updated `tool.uv` section with `asgi-lifespan` in dev-dependencies.

* Convert test fixtures to async and use AsyncClient for HTTP requests

* Handle 'ImportFrom' nodes in AST validation to support module attribute imports

* Convert test cases to use async HTTP client

- Updated test cases in `test_database.py`, `test_endpoints.py`, `test_user.py`, `test_variable.py`, `test_files.py`, `test_chat_endpoint.py`, `test_misc.py`, `test_messages_endpoints.py`, `test_api_key.py`, `test_webhook.py`, and `test_login.py` to use `httpx.AsyncClient` instead of `fastapi.TestClient`.
- Modified test functions to be asynchronous and use `await` for HTTP requests.
- Adjusted fixtures and helper functions to support asynchronous operations.
- Ensured consistency in endpoint paths and request methods across all test cases.

* Refactor string concatenation to f-string in test_chat_endpoint.py

* [autofix.ci] apply automated fixes

* Refactor import validation to use pattern matching for AST nodes

* Set `startup_timeout` and `shutdown_timeout` to `None` in `LifespanManager` for test files.

* Convert test functions to async in `test_messages_endpoints.py`

* Add `api_key_required` marker to assistant component tests

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Gabriel Luiz Freitas Almeida 2024-10-02 14:21:52 -03:00 • committed by GitHub
commit 6febae599b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 444 additions and 322 deletions

View file

@ -1,6 +1,7 @@
from datetime import datetime
import pytest
from httpx import AsyncClient
from langflow.services.auth.utils import create_super_user, get_password_hash
from langflow.services.database.models.user import UserUpdate
@ -22,14 +23,14 @@ def super_user(client):
@pytest.fixture
def super_user_headers(client, super_user):
async def super_user_headers(client: AsyncClient, super_user):
settings_service = get_settings_service()
auth_settings = settings_service.auth_settings
login_data = {
"username": auth_settings.SUPERUSER,
"password": auth_settings.SUPERUSER_PASSWORD,
}
response = client.post("/api/v1/login", data=login_data)
response = await client.post("api/v1/login", data=login_data)
assert response.status_code == 200
tokens = response.json()
a_token = tokens["access_token"]
@ -52,9 +53,8 @@ def deactivated_user():
return user
def test_user_waiting_for_approval(
client,
):
@pytest.mark.api_key_required
async def test_user_waiting_for_approval(client: AsyncClient):
# Create a user that is not active and has never logged in
with session_getter(get_db_service()) as session:
user = User(
@ -67,50 +67,54 @@ def test_user_waiting_for_approval(
session.commit()
login_data = {"username": "waitingforapproval", "password": "testpassword"}
response = client.post("/api/v1/login", data=login_data)
response = await client.post("api/v1/login", data=login_data)
assert response.status_code == 400
assert response.json()["detail"] == "Waiting for approval"
def test_deactivated_user_cannot_login(client, deactivated_user):
@pytest.mark.api_key_required
async def test_deactivated_user_cannot_login(client: AsyncClient, deactivated_user):
login_data = {"username": deactivated_user.username, "password": "testpassword"}
response = client.post("/api/v1/login", data=login_data)
response = await client.post("api/v1/login", data=login_data)
assert response.status_code == 401, response.json()
assert response.json()["detail"] == "Inactive user", response.text
def test_deactivated_user_cannot_access(client, deactivated_user, logged_in_headers):
async def test_deactivated_user_cannot_access(client: AsyncClient, deactivated_user, logged_in_headers):
# Assuming the headers for deactivated_user
response = client.get("/api/v1/users", headers=logged_in_headers)
assert response.status_code == 403, response.json()
response = await client.get("api/v1/users/", headers=logged_in_headers)
assert response.status_code == 403, response.status_code
assert response.json()["detail"] == "The user doesn't have enough privileges", response.text
def test_data_consistency_after_update(client, active_user, logged_in_headers, super_user_headers):
@pytest.mark.api_key_required
async def test_data_consistency_after_update(client: AsyncClient, active_user, logged_in_headers, super_user_headers):
user_id = active_user.id
update_data = UserUpdate(is_active=False)
response = client.patch(f"/api/v1/users/{user_id}", json=update_data.model_dump(), headers=super_user_headers)
response = await client.patch(f"/api/v1/users/{user_id}", json=update_data.model_dump(), headers=super_user_headers)
assert response.status_code == 200, response.json()
# Fetch the updated user from the database
response = client.get("/api/v1/users/whoami", headers=logged_in_headers)
response = await client.get("api/v1/users/whoami", headers=logged_in_headers)
assert response.status_code == 401, response.json()
assert response.json()["detail"] == "User not found or is inactive."
def test_data_consistency_after_delete(client, test_user, super_user_headers):
@pytest.mark.api_key_required
async def test_data_consistency_after_delete(client: AsyncClient, test_user, super_user_headers):
user_id = test_user.get("id")
response = client.delete(f"/api/v1/users/{user_id}", headers=super_user_headers)
response = await client.delete(f"/api/v1/users/{user_id}", headers=super_user_headers)
assert response.status_code == 200, response.json()
# Attempt to fetch the deleted user from the database
response = client.get("/api/v1/users", headers=super_user_headers)
response = await client.get("api/v1/users/", headers=super_user_headers)
assert response.status_code == 200
assert all(user["id"] != user_id for user in response.json()["users"])
def test_inactive_user(client):
@pytest.mark.api_key_required
async def test_inactive_user(client: AsyncClient):
# Create a user that is not active and has a last_login_at value
with session_getter(get_db_service()) as session:
user = User(
@ -123,12 +127,13 @@ def test_inactive_user(client):
session.commit()
login_data = {"username": "inactiveuser", "password": "testpassword"}
response = client.post("/api/v1/login", data=login_data)
response = await client.post("api/v1/login", data=login_data)
assert response.status_code == 401
assert response.json()["detail"] == "Inactive user"
def test_add_user(client, test_user):
@pytest.mark.api_key_required
async def test_add_user(client: AsyncClient, test_user):
assert test_user["username"] == "testuser"
@ -136,51 +141,55 @@ def test_add_user(client, test_user):
# def test_read_current_user(client: TestClient, active_user):
# # First we need to login to get the access token
# login_data = {"username": "testuser", "password": "testpassword"}
# response = client.post("/api/v1/login", data=login_data)
# response = await client.post("api/v1/login", data=login_data)
# assert response.status_code == 200
# headers = {"Authorization": f"Bearer {response.json()['access_token']}"}
# response = client.get("/api/v1/user", headers=headers)
# response = await client.get("api/v1/user", headers=headers)
# assert response.status_code == 200, response.json()
# assert response.json()["username"] == "testuser"
def test_read_all_users(client, super_user_headers):
response = client.get("/api/v1/users", headers=super_user_headers)
@pytest.mark.api_key_required
async def test_read_all_users(client: AsyncClient, super_user_headers):
response = await client.get("api/v1/users/", headers=super_user_headers)
assert response.status_code == 200, response.json()
assert isinstance(response.json()["users"], list)
def test_normal_user_cant_read_all_users(client, logged_in_headers):
response = client.get("/api/v1/users", headers=logged_in_headers)
@pytest.mark.api_key_required
async def test_normal_user_cant_read_all_users(client: AsyncClient, logged_in_headers):
response = await client.get("api/v1/users/", headers=logged_in_headers)
assert response.status_code == 403, response.json()
assert response.json() == {"detail": "The user doesn't have enough privileges"}
def test_patch_user(client, active_user, logged_in_headers):
@pytest.mark.api_key_required
async def test_patch_user(client: AsyncClient, active_user, logged_in_headers):
user_id = active_user.id
update_data = UserUpdate(
username="newname",
)
response = client.patch(f"/api/v1/users/{user_id}", json=update_data.model_dump(), headers=logged_in_headers)
response = await client.patch(f"/api/v1/users/{user_id}", json=update_data.model_dump(), headers=logged_in_headers)
assert response.status_code == 200, response.json()
update_data = UserUpdate(
profile_image="new_image",
)
response = client.patch(f"/api/v1/users/{user_id}", json=update_data.model_dump(), headers=logged_in_headers)
response = await client.patch(f"/api/v1/users/{user_id}", json=update_data.model_dump(), headers=logged_in_headers)
assert response.status_code == 200, response.json()
def test_patch_reset_password(client, active_user, logged_in_headers):
@pytest.mark.api_key_required
async def test_patch_reset_password(client: AsyncClient, active_user, logged_in_headers):
user_id = active_user.id
update_data = UserUpdate(
password="newpassword",
)
response = client.patch(
response = await client.patch(
f"/api/v1/users/{user_id}/reset-password",
json=update_data.model_dump(),
headers=logged_in_headers,
@ -188,17 +197,18 @@ def test_patch_reset_password(client, active_user, logged_in_headers):
assert response.status_code == 200, response.json()
# Now we need to test if the new password works
login_data = {"username": active_user.username, "password": "newpassword"}
response = client.post("/api/v1/login", data=login_data)
response = await client.post("api/v1/login", data=login_data)
assert response.status_code == 200
def test_patch_user_wrong_id(client, active_user, logged_in_headers):
@pytest.mark.api_key_required
async def test_patch_user_wrong_id(client: AsyncClient, active_user, logged_in_headers):
user_id = "wrong_id"
update_data = UserUpdate(
username="newname",
)
response = client.patch(f"/api/v1/users/{user_id}", json=update_data.model_dump(), headers=logged_in_headers)
response = await client.patch(f"/api/v1/users/{user_id}", json=update_data.model_dump(), headers=logged_in_headers)
assert response.status_code == 422, response.json()
json_response = response.json()
detail = json_response["detail"]
@ -207,16 +217,18 @@ def test_patch_user_wrong_id(client, active_user, logged_in_headers):
assert error["type"] == "uuid_parsing"
def test_delete_user(client, test_user, super_user_headers):
@pytest.mark.api_key_required
async def test_delete_user(client: AsyncClient, test_user, super_user_headers):
user_id = test_user["id"]
response = client.delete(f"/api/v1/users/{user_id}", headers=super_user_headers)
response = await client.delete(f"/api/v1/users/{user_id}", headers=super_user_headers)
assert response.status_code == 200
assert response.json() == {"detail": "User deleted"}
def test_delete_user_wrong_id(client, test_user, super_user_headers):
@pytest.mark.api_key_required
async def test_delete_user_wrong_id(client: AsyncClient, test_user, super_user_headers):
user_id = "wrong_id"
response = client.delete(f"/api/v1/users/{user_id}", headers=super_user_headers)
response = await client.delete(f"/api/v1/users/{user_id}", headers=super_user_headers)
assert response.status_code == 422
json_response = response.json()
detail = json_response["detail"]
@ -225,8 +237,9 @@ def test_delete_user_wrong_id(client, test_user, super_user_headers):
assert error["type"] == "uuid_parsing"
def test_normal_user_cant_delete_user(client, test_user, logged_in_headers):
@pytest.mark.api_key_required
async def test_normal_user_cant_delete_user(client: AsyncClient, test_user, logged_in_headers):
user_id = test_user["id"]
response = client.delete(f"/api/v1/users/{user_id}", headers=logged_in_headers)
response = await client.delete(f"/api/v1/users/{user_id}", headers=logged_in_headers)
assert response.status_code == 403
assert response.json() == {"detail": "The user doesn't have enough privileges"}