test: add unit tests for routes (#4249)

This commit is contained in:
Ítalo Johnny 2024-10-28 17:06:05 -03:00 • committed by GitHub
commit fadb20115d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 623 additions and 272 deletions

View file

@ -19,7 +19,7 @@ router = APIRouter(tags=["APIKey"], prefix="/api_key")
@router.get("/") @router.get("/")
def get_api_keys_route( async def get_api_keys_route(
db: DbSession, db: DbSession,
current_user: CurrentActiveUser, current_user: CurrentActiveUser,
) -> ApiKeysResponse: ) -> ApiKeysResponse:
@ -33,7 +33,7 @@ def get_api_keys_route(
@router.post("/") @router.post("/")
def create_api_key_route( async def create_api_key_route(
req: ApiKeyCreate, req: ApiKeyCreate,
current_user: CurrentActiveUser, current_user: CurrentActiveUser,
db: DbSession, db: DbSession,
@ -46,7 +46,7 @@ def create_api_key_route(
@router.delete("/{api_key_id}", dependencies=[Depends(auth_utils.get_current_active_user)]) @router.delete("/{api_key_id}", dependencies=[Depends(auth_utils.get_current_active_user)])
def delete_api_key_route( async def delete_api_key_route(
api_key_id: UUID, api_key_id: UUID,
db: DbSession, db: DbSession,
): ):
@ -58,7 +58,7 @@ def delete_api_key_route(
@router.post("/store") @router.post("/store")
def save_store_api_key( async def save_store_api_key(
api_key_request: ApiKeyCreateRequest, api_key_request: ApiKeyCreateRequest,
response: Response, response: Response,
current_user: CurrentActiveUser, current_user: CurrentActiveUser,
@ -90,17 +90,3 @@ def save_store_api_key(
raise HTTPException(status_code=400, detail=str(e)) from e raise HTTPException(status_code=400, detail=str(e)) from e
return {"detail": "API Key saved"} return {"detail": "API Key saved"}
@router.delete("/store")
def delete_store_api_key(
current_user: CurrentActiveUser,
db: DbSession,
):
try:
current_user.store_api_key = None
db.commit()
except Exception as e:
raise HTTPException(status_code=400, detail=str(e)) from e
return {"detail": "API Key deleted"}

View file

@ -549,7 +549,7 @@ async def create_upload_file(
# get endpoint to return version of langflow # get endpoint to return version of langflow
@router.get("/version") @router.get("/version")
def get_version(): async def get_version():
return get_version_info() return get_version_info()
@ -625,7 +625,7 @@ async def custom_component_update(
@router.get("/config", response_model=ConfigResponse) @router.get("/config", response_model=ConfigResponse)
def get_config(): async def get_config():
try: try:
from langflow.services.deps import get_settings_service from langflow.services.deps import get_settings_service
@ -637,5 +637,5 @@ def get_config():
@router.get("/sidebar_categories") @router.get("/sidebar_categories")
def get_sidebar_categories() -> SidebarCategoriesResponse: async def get_sidebar_categories() -> SidebarCategoriesResponse:
return SidebarCategoriesResponse(categories=SIDEBAR_CATEGORIES) return SidebarCategoriesResponse(categories=SIDEBAR_CATEGORIES)

View file

@ -35,7 +35,7 @@ router = APIRouter(prefix="/flows", tags=["Flows"])
@router.post("/", response_model=FlowRead, status_code=201) @router.post("/", response_model=FlowRead, status_code=201)
def create_flow( async def create_flow(
*, *,
session: DbSession, session: DbSession,
flow: FlowCreate, flow: FlowCreate,
@ -124,7 +124,7 @@ def create_flow(
@router.get("/", response_model=list[FlowRead] | Page[FlowRead] | list[FlowHeader], status_code=200) @router.get("/", response_model=list[FlowRead] | Page[FlowRead] | list[FlowHeader], status_code=200)
def read_flows( async def read_flows(
*, *,
current_user: CurrentActiveUser, current_user: CurrentActiveUser,
session: DbSession, session: DbSession,
@ -226,7 +226,7 @@ def _read_flow(
@router.get("/{flow_id}", response_model=FlowRead, status_code=200) @router.get("/{flow_id}", response_model=FlowRead, status_code=200)
def read_flow( async def read_flow(
*, *,
session: DbSession, session: DbSession,
flow_id: UUID, flow_id: UUID,
@ -239,7 +239,7 @@ def read_flow(
@router.patch("/{flow_id}", response_model=FlowRead, status_code=200) @router.patch("/{flow_id}", response_model=FlowRead, status_code=200)
def update_flow( async def update_flow(
*, *,
session: DbSession, session: DbSession,
flow_id: UUID, flow_id: UUID,
@ -320,7 +320,7 @@ async def delete_flow(
@router.post("/batch/", response_model=list[FlowRead], status_code=201) @router.post("/batch/", response_model=list[FlowRead], status_code=201)
def create_flows( async def create_flows(
*, *,
session: DbSession, session: DbSession,
flow_list: FlowListCreate, flow_list: FlowListCreate,
@ -357,7 +357,7 @@ async def upload_file(
flow.user_id = current_user.id flow.user_id = current_user.id
if folder_id: if folder_id:
flow.folder_id = folder_id flow.folder_id = folder_id
response = create_flow(session=session, flow=flow, current_user=current_user) response = await create_flow(session=session, flow=flow, current_user=current_user)
response_list.append(response) response_list.append(response)
return response_list return response_list
@ -442,7 +442,7 @@ async def download_multiple_file(
@router.get("/basic_examples/", response_model=list[FlowRead], status_code=200) @router.get("/basic_examples/", response_model=list[FlowRead], status_code=200)
def read_basic_examples( async def read_basic_examples(
*, *,
session: DbSession, session: DbSession,
): ):

View file

@ -28,7 +28,7 @@ router = APIRouter(prefix="/folders", tags=["Folders"])
@router.post("/", response_model=FolderRead, status_code=201) @router.post("/", response_model=FolderRead, status_code=201)
def create_folder( async def create_folder(
*, *,
session: DbSession, session: DbSession,
folder: FolderCreate, folder: FolderCreate,
@ -82,7 +82,7 @@ def create_folder(
@router.get("/", response_model=list[FolderRead], status_code=200) @router.get("/", response_model=list[FolderRead], status_code=200)
def read_folders( async def read_folders(
*, *,
session: DbSession, session: DbSession,
current_user: CurrentActiveUser, current_user: CurrentActiveUser,
@ -100,7 +100,7 @@ def read_folders(
@router.get("/{folder_id}", response_model=FolderWithPaginatedFlows | FolderReadWithFlows, status_code=200) @router.get("/{folder_id}", response_model=FolderWithPaginatedFlows | FolderReadWithFlows, status_code=200)
def read_folder( async def read_folder(
*, *,
session: DbSession, session: DbSession,
folder_id: str, folder_id: str,
@ -145,7 +145,7 @@ def read_folder(
@router.patch("/{folder_id}", response_model=FolderRead, status_code=200) @router.patch("/{folder_id}", response_model=FolderRead, status_code=200)
def update_folder( async def update_folder(
*, *,
session: DbSession, session: DbSession,
folder_id: str, folder_id: str,

View file

@ -7,7 +7,7 @@ router = APIRouter(prefix="/starter-projects", tags=["Flows"])
@router.get("/", dependencies=[Depends(get_current_active_user)], status_code=200) @router.get("/", dependencies=[Depends(get_current_active_user)], status_code=200)
def get_starter_projects() -> list[GraphDump]: async def get_starter_projects() -> list[GraphDump]:
"""Get a list of starter projects.""" """Get a list of starter projects."""
from langflow.initial_setup.load import get_starter_projects_dump from langflow.initial_setup.load import get_starter_projects_dump

View file

@ -40,7 +40,7 @@ def get_optional_user_store_api_key(user: CurrentActiveUser):
@router.get("/check/") @router.get("/check/")
def check_if_store_is_enabled(): async def check_if_store_is_enabled():
return { return {
"enabled": get_settings_service().settings.store, "enabled": get_settings_service().settings.store,
} }

View file

@ -23,7 +23,7 @@ router = APIRouter(tags=["Users"], prefix="/users")
@router.post("/", response_model=UserRead, status_code=201) @router.post("/", response_model=UserRead, status_code=201)
def add_user( async def add_user(
user: UserCreate, user: UserCreate,
session: DbSession, session: DbSession,
) -> User: ) -> User:
@ -46,7 +46,7 @@ def add_user(
@router.get("/whoami", response_model=UserRead) @router.get("/whoami", response_model=UserRead)
def read_current_user( async def read_current_user(
current_user: CurrentActiveUser, current_user: CurrentActiveUser,
) -> User: ) -> User:
"""Retrieve the current user's data.""" """Retrieve the current user's data."""
@ -54,7 +54,7 @@ def read_current_user(
@router.get("/", dependencies=[Depends(get_current_active_superuser)]) @router.get("/", dependencies=[Depends(get_current_active_superuser)])
def read_all_users( async def read_all_users(
*, *,
skip: int = 0, skip: int = 0,
limit: int = 10, limit: int = 10,
@ -74,7 +74,7 @@ def read_all_users(
@router.patch("/{user_id}", response_model=UserRead) @router.patch("/{user_id}", response_model=UserRead)
def patch_user( async def patch_user(
user_id: UUID, user_id: UUID,
user_update: UserUpdate, user_update: UserUpdate,
user: CurrentActiveUser, user: CurrentActiveUser,
@ -101,7 +101,7 @@ def patch_user(
@router.patch("/{user_id}/reset-password", response_model=UserRead) @router.patch("/{user_id}/reset-password", response_model=UserRead)
def reset_password( async def reset_password(
user_id: UUID, user_id: UUID,
user_update: UserUpdate, user_update: UserUpdate,
user: CurrentActiveUser, user: CurrentActiveUser,
@ -124,7 +124,7 @@ def reset_password(
@router.delete("/{user_id}") @router.delete("/{user_id}")
def delete_user( async def delete_user(
user_id: UUID, user_id: UUID,
current_user: Annotated[User, Depends(get_current_active_superuser)], current_user: Annotated[User, Depends(get_current_active_superuser)],
session: DbSession, session: DbSession,

View file

@ -10,7 +10,7 @@ router = APIRouter(prefix="/validate", tags=["Validate"])
@router.post("/code", status_code=200) @router.post("/code", status_code=200)
def post_validate_code(code: Code) -> CodeValidationResponse: async def post_validate_code(code: Code) -> CodeValidationResponse:
try: try:
errors = validate_code(code.code) errors = validate_code(code.code)
return CodeValidationResponse( return CodeValidationResponse(
@ -23,7 +23,7 @@ def post_validate_code(code: Code) -> CodeValidationResponse:
@router.post("/prompt", status_code=200) @router.post("/prompt", status_code=200)
def post_validate_prompt(prompt_request: ValidatePromptRequest) -> PromptValidationResponse: async def post_validate_prompt(prompt_request: ValidatePromptRequest) -> PromptValidationResponse:
try: try:
if not prompt_request.frontend_node: if not prompt_request.frontend_node:
return PromptValidationResponse( return PromptValidationResponse(

View file

@ -13,7 +13,7 @@ router = APIRouter(prefix="/variables", tags=["Variables"])
@router.post("/", response_model=VariableRead, status_code=201) @router.post("/", response_model=VariableRead, status_code=201)
def create_variable( async def create_variable(
*, *,
session: DbSession, session: DbSession,
variable: VariableCreate, variable: VariableCreate,
@ -48,7 +48,7 @@ def create_variable(
@router.get("/", response_model=list[VariableRead], status_code=200) @router.get("/", response_model=list[VariableRead], status_code=200)
def read_variables( async def read_variables(
*, *,
session: DbSession, session: DbSession,
current_user: CurrentActiveUser, current_user: CurrentActiveUser,
@ -65,7 +65,7 @@ def read_variables(
@router.patch("/{variable_id}", response_model=VariableRead, status_code=200) @router.patch("/{variable_id}", response_model=VariableRead, status_code=200)
def update_variable( async def update_variable(
*, *,
session: DbSession, session: DbSession,
variable_id: UUID, variable_id: UUID,
@ -92,7 +92,7 @@ def update_variable(
@router.delete("/{variable_id}", status_code=204) @router.delete("/{variable_id}", status_code=204)
def delete_variable( async def delete_variable(
*, *,
session: DbSession, session: DbSession,
variable_id: UUID, variable_id: UUID,

View file

@ -384,6 +384,44 @@ async def logged_in_headers(client, active_user):
return {"Authorization": f"Bearer {a_token}"} return {"Authorization": f"Bearer {a_token}"}
@pytest.fixture
def active_super_user(client): # noqa: ARG001
db_manager = get_db_service()
with db_manager.with_session() as session:
user = User(
username="activeuser",
password=get_password_hash("testpassword"),
is_active=True,
is_superuser=True,
)
if active_user := session.exec(select(User).where(User.username == user.username)).first():
user = active_user
else:
session.add(user)
session.commit()
session.refresh(user)
user = UserRead.model_validate(user, from_attributes=True)
yield user
# Clean up
# Now cleanup transactions, vertex_build
with db_manager.with_session() as session:
user = session.get(User, user.id)
_delete_transactions_and_vertex_builds(session, user)
session.delete(user)
session.commit()
@pytest.fixture
async def logged_in_headers_super_user(client, active_super_user):
login_data = {"username": active_super_user.username, "password": "testpassword"}
response = await client.post("api/v1/login", data=login_data)
assert response.status_code == 200
tokens = response.json()
a_token = tokens["access_token"]
return {"Authorization": f"Bearer {a_token}"}
@pytest.fixture @pytest.fixture
def flow( def flow(
client, # noqa: ARG001 client, # noqa: ARG001
@ -484,7 +522,7 @@ async def added_webhook_test(client, json_webhook_test, logged_in_headers):
@pytest.fixture @pytest.fixture
async def flow_component(client: TestClient, logged_in_headers): async def flow_component(client: AsyncClient, logged_in_headers):
chat_input = ChatInput() chat_input = ChatInput()
graph = Graph(start=chat_input, end=chat_input) graph = Graph(start=chat_input, end=chat_input)
graph_dict = graph.dump(name="Chat Input Component") graph_dict = graph.dump(name="Chat Input Component")

View file

@ -2,14 +2,14 @@ from uuid import uuid4
import pytest import pytest
from fastapi import status from fastapi import status
from fastapi.testclient import TestClient from httpx import AsyncClient
from langflow.graph.schema import RunOutputs from langflow.graph.schema import RunOutputs
from langflow.initial_setup.setup import load_starter_projects from langflow.initial_setup.setup import load_starter_projects
from langflow.load import run_flow_from_json from langflow.load import run_flow_from_json
@pytest.mark.api_key_required @pytest.mark.api_key_required
async def test_run_flow_with_caching_success(client: TestClient, starter_project, created_api_key): async def test_run_flow_with_caching_success(client: AsyncClient, starter_project, created_api_key):
flow_id = starter_project["id"] flow_id = starter_project["id"]
headers = {"x-api-key": created_api_key.api_key} headers = {"x-api-key": created_api_key.api_key}
payload = { payload = {
@ -27,7 +27,7 @@ async def test_run_flow_with_caching_success(client: TestClient, starter_project
@pytest.mark.api_key_required @pytest.mark.api_key_required
async def test_run_flow_with_caching_invalid_flow_id(client: TestClient, created_api_key): async def test_run_flow_with_caching_invalid_flow_id(client: AsyncClient, created_api_key):
invalid_flow_id = uuid4() invalid_flow_id = uuid4()
headers = {"x-api-key": created_api_key.api_key} headers = {"x-api-key": created_api_key.api_key}
payload = {"input_value": "", "input_type": "text", "output_type": "text", "tweaks": {}, "stream": False} payload = {"input_value": "", "input_type": "text", "output_type": "text", "tweaks": {}, "stream": False}
@ -39,7 +39,7 @@ async def test_run_flow_with_caching_invalid_flow_id(client: TestClient, created
@pytest.mark.api_key_required @pytest.mark.api_key_required
async def test_run_flow_with_caching_invalid_input_format(client: TestClient, starter_project, created_api_key): async def test_run_flow_with_caching_invalid_input_format(client: AsyncClient, starter_project, created_api_key):
flow_id = starter_project["id"] flow_id = starter_project["id"]
headers = {"x-api-key": created_api_key.api_key} headers = {"x-api-key": created_api_key.api_key}
payload = {"input_value": {"key": "value"}, "input_type": "text", "output_type": "text", "tweaks": {}} payload = {"input_value": {"key": "value"}, "input_type": "text", "output_type": "text", "tweaks": {}}

View file

@ -0,0 +1,64 @@
from fastapi import status
from httpx import AsyncClient
async def test_create_folder(client: AsyncClient, logged_in_headers):
response = await client.get("api/v1/api_key/", headers=logged_in_headers)
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, dict), "The result must be a dictionary"
assert "api_keys" in result, "The dictionary must contain a key called 'api_keys'"
assert "user_id" in result, "The dictionary must contain a key called 'user_id'"
assert "total_count" in result, "The dictionary must contain a key called 'total_count'"
async def test_create_api_key_route(client: AsyncClient, logged_in_headers, active_user):
basic_case = {
"name": "string",
"total_uses": 0,
"is_active": True,
"api_key": "string",
"user_id": str(active_user.id),
}
response = await client.post("api/v1/api_key/", json=basic_case, headers=logged_in_headers)
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, dict), "The result must be a dictionary"
assert "api_key" in result, "The dictionary must contain a key called 'api_key'"
assert "id" in result, "The dictionary must contain a key called 'id'"
assert "is_active" in result, "The dictionary must contain a key called 'is_active'"
assert "last_used_at" in result, "The dictionary must contain a key called 'last_used_at'"
assert "name" in result, "The dictionary must contain a key called 'name'"
assert "total_uses" in result, "The dictionary must contain a key called 'total_uses'"
assert "user_id" in result, "The dictionary must contain a key called 'user_id'"
async def test_delete_api_key_route(client: AsyncClient, logged_in_headers, active_user):
basic_case = {
"name": "string",
"total_uses": 0,
"is_active": True,
"api_key": "string",
"user_id": str(active_user.id),
}
_response = await client.post("api/v1/api_key/", json=basic_case, headers=logged_in_headers)
_id = _response.json()["id"]
response = await client.delete(f"api/v1/api_key/{_id}", headers=logged_in_headers)
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, dict), "The result must be a dictionary"
assert "detail" in result, "The dictionary must contain a key called 'detail'"
async def test_save_store_api_key(client: AsyncClient, logged_in_headers):
basic_case = {"api_key": "string"}
response = await client.post("api/v1/api_key/store", json=basic_case, headers=logged_in_headers)
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, dict), "The result must be a dictionary"
assert "detail" in result, "The dictionary must contain a key called 'detail'"

View file

@ -0,0 +1,36 @@
from fastapi import status
from httpx import AsyncClient
async def test_get_version(client: AsyncClient):
response = await client.get("api/v1/version")
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, dict), "The result must be a dictionary"
assert "version" in result, "The dictionary must contain a key called 'version'"
assert "main_version" in result, "The dictionary must contain a key called 'main_version'"
assert "package" in result, "The dictionary must contain a key called 'package'"
async def test_get_config(client: AsyncClient):
response = await client.get("api/v1/config")
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, dict), "The result must be a dictionary"
assert "frontend_timeout" in result, "The dictionary must contain a key called 'frontend_timeout'"
assert "auto_saving" in result, "The dictionary must contain a key called 'auto_saving'"
assert "health_check_max_retries" in result, "The dictionary must contain a 'health_check_max_retries' key"
assert "max_file_size_upload" in result, "The dictionary must contain a key called 'max_file_size_upload'"
async def test_get_sidebar_components(client: AsyncClient):
response = await client.get("api/v1/sidebar_categories")
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, dict), "The result must be a dictionary"
assert "categories" in result, "The dictionary must contain a key called 'categories'"
assert len(result["categories"]) > 0, "The categories list must not be empty"
assert isinstance(result["categories"], list), "The categories must be a list"

View file

@ -0,0 +1,172 @@
from fastapi import status
from httpx import AsyncClient
async def test_create_flow(client: AsyncClient, logged_in_headers):
basic_case = {
"name": "string",
"description": "string",
"icon": "string",
"icon_bg_color": "#ff00ff",
"gradient": "string",
"data": {},
"is_component": False,
"webhook": False,
"endpoint_name": "string",
"tags": ["string"],
"user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"folder_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
}
response = await client.post("api/v1/flows/", json=basic_case, headers=logged_in_headers)
result = response.json()
assert response.status_code == status.HTTP_201_CREATED
assert isinstance(result, dict), "The result must be a dictionary"
assert "data" in result, "The result must have a 'data' key"
assert "description" in result, "The result must have a 'description' key"
assert "endpoint_name" in result, "The result must have a 'endpoint_name' key"
assert "folder_id" in result, "The result must have a 'folder_id' key"
assert "gradient" in result, "The result must have a 'gradient' key"
assert "icon" in result, "The result must have a 'icon' key"
assert "icon_bg_color" in result, "The result must have a 'icon_bg_color' key"
assert "id" in result, "The result must have a 'id' key"
assert "is_component" in result, "The result must have a 'is_component' key"
assert "name" in result, "The result must have a 'name' key"
assert "tags" in result, "The result must have a 'tags' key"
assert "updated_at" in result, "The result must have a 'updated_at' key"
assert "user_id" in result, "The result must have a 'user_id' key"
assert "webhook" in result, "The result must have a 'webhook' key"
async def test_read_flows(client: AsyncClient, logged_in_headers):
params = {
"remove_example_flows": False,
"components_only": False,
"get_all": True,
"header_flows": False,
"page": 1,
"size": 50,
}
response = await client.get("api/v1/flows/", params=params, headers=logged_in_headers)
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, list), "The result must be a list"
async def test_read_flow(client: AsyncClient, logged_in_headers):
basic_case = {
"name": "string",
"description": "string",
"icon": "string",
"icon_bg_color": "#ff00ff",
"gradient": "string",
"data": {},
"is_component": False,
"webhook": False,
"endpoint_name": "string",
"tags": ["string"],
"user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"folder_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
}
_response = await client.post("api/v1/flows/", json=basic_case, headers=logged_in_headers)
_id = _response.json()["id"]
response = await client.get(f"api/v1/flows/{_id}", headers=logged_in_headers)
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, dict), "The result must be a dictionary"
assert "data" in result, "The result must have a 'data' key"
assert "description" in result, "The result must have a 'description' key"
assert "endpoint_name" in result, "The result must have a 'endpoint_name' key"
assert "folder_id" in result, "The result must have a 'folder_id' key"
assert "gradient" in result, "The result must have a 'gradient' key"
assert "icon" in result, "The result must have a 'icon' key"
assert "icon_bg_color" in result, "The result must have a 'icon_bg_color' key"
assert "id" in result, "The result must have a 'id' key"
assert "is_component" in result, "The result must have a 'is_component' key"
assert "name" in result, "The result must have a 'name' key"
assert "tags" in result, "The result must have a 'tags' key"
assert "updated_at" in result, "The result must have a 'updated_at' key"
assert "user_id" in result, "The result must have a 'user_id' key"
assert "webhook" in result, "The result must have a 'webhook' key"
async def test_update_flow(client: AsyncClient, logged_in_headers):
name = "first_name"
updated_name = "second_name"
basic_case = {
"description": "string",
"icon": "string",
"icon_bg_color": "#ff00ff",
"gradient": "string",
"data": {},
"is_component": False,
"webhook": False,
"endpoint_name": "string",
"tags": ["string"],
"user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"folder_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
}
basic_case["name"] = name
_response = await client.post("api/v1/flows/", json=basic_case, headers=logged_in_headers)
_id = _response.json()["id"]
basic_case["name"] = updated_name
response = await client.patch(f"api/v1/flows/{_id}", json=basic_case, headers=logged_in_headers)
result = response.json()
assert isinstance(result, dict), "The result must be a dictionary"
assert "data" in result, "The result must have a 'data' key"
assert "description" in result, "The result must have a 'description' key"
assert "endpoint_name" in result, "The result must have a 'endpoint_name' key"
assert "folder_id" in result, "The result must have a 'folder_id' key"
assert "gradient" in result, "The result must have a 'gradient' key"
assert "icon" in result, "The result must have a 'icon' key"
assert "icon_bg_color" in result, "The result must have a 'icon_bg_color' key"
assert "id" in result, "The result must have a 'id' key"
assert "is_component" in result, "The result must have a 'is_component' key"
assert "name" in result, "The result must have a 'name' key"
assert "tags" in result, "The result must have a 'tags' key"
assert "updated_at" in result, "The result must have a 'updated_at' key"
assert "user_id" in result, "The result must have a 'user_id' key"
assert "webhook" in result, "The result must have a 'webhook' key"
assert result["name"] == updated_name, "The name must be updated"
async def test_create_flows(client: AsyncClient, logged_in_headers):
amount_flows = 10
basic_case = {
"description": "string",
"icon": "string",
"icon_bg_color": "#ff00ff",
"gradient": "string",
"data": {},
"is_component": False,
"webhook": False,
"tags": ["string"],
"user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"folder_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
}
cases = []
for i in range(amount_flows):
case = basic_case.copy()
case["name"] = f"string_{i}"
case["endpoint_name"] = f"string_{i}"
cases.append(case)
response = await client.post("api/v1/flows/batch/", json={"flows": cases}, headers=logged_in_headers)
result = response.json()
assert response.status_code == status.HTTP_201_CREATED
assert isinstance(result, list), "The result must be a list"
assert len(result) == amount_flows, "The result must have the same amount of flows"
async def test_read_basic_examples(client: AsyncClient, logged_in_headers):
response = await client.get("api/v1/flows/basic_examples/", headers=logged_in_headers)
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, list), "The result must be a list"
assert len(result) > 0, "The result must have at least one flow"

View file

@ -0,0 +1,64 @@
import pytest
from fastapi import status
from httpx import AsyncClient
@pytest.fixture
def basic_case():
return {
"name": "New Folder",
"description": "",
"flows_list": [],
"components_list": [],
}
async def test_create_folder(client: AsyncClient, logged_in_headers, basic_case):
response = await client.post("api/v1/folders/", json=basic_case, headers=logged_in_headers)
result = response.json()
assert response.status_code == status.HTTP_201_CREATED
assert isinstance(result, dict), "The result must be a dictionary"
assert "name" in result, "The dictionary must contain a key called 'name'"
assert "description" in result, "The dictionary must contain a key called 'description'"
assert "id" in result, "The dictionary must contain a key called 'id'"
assert "parent_id" in result, "The dictionary must contain a key called 'parent_id'"
async def test_read_folders(client: AsyncClient, logged_in_headers):
response = await client.get("api/v1/folders/", headers=logged_in_headers)
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, list), "The result must be a list"
assert len(result) > 0, "The list must not be empty"
async def test_read_folder(client: AsyncClient, logged_in_headers, basic_case):
_response = await client.post("api/v1/folders/", json=basic_case, headers=logged_in_headers)
_id = _response.json()["id"]
response = await client.get(f"api/v1/folders/{_id}", headers=logged_in_headers)
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, dict), "The result must be a dictionary"
assert "name" in result, "The dictionary must contain a key called 'name'"
assert "description" in result, "The dictionary must contain a key called 'description'"
assert "id" in result, "The dictionary must contain a key called 'id'"
assert "parent_id" in result, "The dictionary must contain a key called 'parent_id'"
async def test_update_folder(client: AsyncClient, logged_in_headers, basic_case):
update_case = basic_case.copy()
update_case["name"] = "Updated Folder"
_response = await client.post("api/v1/folders/", json=basic_case, headers=logged_in_headers)
_id = _response.json()["id"]
response = await client.patch(f"api/v1/folders/{_id}", json=update_case, headers=logged_in_headers)
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, dict), "The result must be a dictionary"
assert "name" in result, "The dictionary must contain a key called 'name'"
assert "description" in result, "The dictionary must contain a key called 'description'"
assert "id" in result, "The dictionary must contain a key called 'id'"
assert "parent_id" in result, "The dictionary must contain a key called 'parent_id'"

View file

@ -0,0 +1,10 @@
from fastapi import status
from httpx import AsyncClient
async def test_get_starter_projects(client: AsyncClient, logged_in_headers):
response = await client.get("api/v1/starter-projects/", headers=logged_in_headers)
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, list), "The result must be a list"

View file

@ -0,0 +1,12 @@
from fastapi import status
from httpx import AsyncClient
async def test_check_if_store_is_enabled(client: AsyncClient):
response = await client.get("api/v1/store/check/")
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, dict), "The variable must be a dictionary"
assert "enabled" in result, "The dictionary must contain a key called 'enabled'"
assert isinstance(result["enabled"], bool), "There must be a boolean value for the key 'enabled' in the dictionary"

View file

@ -0,0 +1,98 @@
from fastapi import status
from httpx import AsyncClient
async def test_add_user(client: AsyncClient):
basic_case = {"username": "string", "password": "string"}
response = await client.post("api/v1/users/", json=basic_case)
result = response.json()
assert response.status_code == status.HTTP_201_CREATED
assert isinstance(result, dict), "The result must be a dictionary"
assert "id" in result, "The result must have an 'id' key"
assert "is_active" in result, "The result must have an 'is_active' key"
assert "is_superuser" in result, "The result must have an 'is_superuser' key"
assert "last_login_at" in result, "The result must have an 'last_login_at' key"
assert "profile_image" in result, "The result must have an 'profile_image' key"
assert "store_api_key" in result, "The result must have an 'store_api_key' key"
assert "updated_at" in result, "The result must have an 'updated_at' key"
assert "username" in result, "The result must have an 'username' key"
async def test_read_current_user(client: AsyncClient, logged_in_headers):
response = await client.get("api/v1/users/whoami", headers=logged_in_headers)
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, dict), "The result must be a dictionary"
assert "id" in result, "The result must have an 'id' key"
assert "is_active" in result, "The result must have an 'is_active' key"
assert "is_superuser" in result, "The result must have an 'is_superuser' key"
assert "last_login_at" in result, "The result must have an 'last_login_at' key"
assert "profile_image" in result, "The result must have an 'profile_image' key"
assert "store_api_key" in result, "The result must have an 'store_api_key' key"
assert "updated_at" in result, "The result must have an 'updated_at' key"
assert "username" in result, "The result must have an 'username' key"
async def test_read_all_users(client: AsyncClient, logged_in_headers_super_user):
response = await client.get("api/v1/users/", headers=logged_in_headers_super_user)
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, dict), "The result must be a dictionary"
assert "total_count" in result, "The result must have an 'total_count' key"
assert "users" in result, "The result must have an 'users' key"
async def test_patch_user(client: AsyncClient, logged_in_headers_super_user):
name = "string"
updated_name = "string2"
basic_case = {"username": name, "password": "string"}
_response = await client.post("api/v1/users/", json=basic_case)
_id = _response.json()["id"]
basic_case["username"] = updated_name
response = await client.patch(f"api/v1/users/{_id}", json=basic_case, headers=logged_in_headers_super_user)
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, dict), "The result must be a dictionary"
assert "id" in result, "The result must have an 'id' key"
assert "is_active" in result, "The result must have an 'is_active' key"
assert "is_superuser" in result, "The result must have an 'is_superuser' key"
assert "last_login_at" in result, "The result must have an 'last_login_at' key"
assert "profile_image" in result, "The result must have an 'profile_image' key"
assert "store_api_key" in result, "The result must have an 'store_api_key' key"
assert "updated_at" in result, "The result must have an 'updated_at' key"
assert "username" in result, "The result must have an 'username' key"
assert result["username"] == updated_name, "The username must be updated"
async def test_reset_password(client: AsyncClient, logged_in_headers, active_user):
_id = str(active_user.id)
basic_case = {"username": "string", "password": "new_password"}
response = await client.patch(f"api/v1/users/{_id}/reset-password", json=basic_case, headers=logged_in_headers)
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, dict), "The result must be a dictionary"
assert "id" in result, "The result must have an 'id' key"
assert "is_active" in result, "The result must have an 'is_active' key"
assert "is_superuser" in result, "The result must have an 'is_superuser' key"
assert "last_login_at" in result, "The result must have an 'last_login_at' key"
assert "profile_image" in result, "The result must have an 'profile_image' key"
assert "store_api_key" in result, "The result must have an 'store_api_key' key"
assert "updated_at" in result, "The result must have an 'updated_at' key"
assert "username" in result, "The result must have an 'username' key"
async def test_delete_user(client: AsyncClient, logged_in_headers_super_user):
basic_case = {"username": "string", "password": "string"}
_response = await client.post("api/v1/users/", json=basic_case)
_id = _response.json()["id"]
response = await client.delete(f"api/v1/users/{_id}", headers=logged_in_headers_super_user)
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, dict), "The result must be a dictionary"
assert "detail" in result, "The result must have an 'detail' key"

View file

@ -0,0 +1,56 @@
from fastapi import status
from httpx import AsyncClient
async def test_post_validate_code(client: AsyncClient):
good_code = """
from pprint import pprint
var = {"a": 1, "b": 2}
pprint(var)
"""
response = await client.post("api/v1/validate/code", json={"code": good_code})
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, dict), "The result must be a dictionary"
assert "imports" in result, "The result must have an 'imports' key"
assert "function" in result, "The result must have a 'function' key"
async def test_post_validate_prompt(client: AsyncClient):
basic_case = {
"name": "string",
"template": "string",
"custom_fields": {},
"frontend_node": {
"template": {},
"description": "string",
"icon": "string",
"is_input": True,
"is_output": True,
"is_composition": True,
"base_classes": ["string"],
"name": "",
"display_name": "",
"documentation": "",
"custom_fields": {},
"output_types": [],
"full_path": "string",
"pinned": False,
"conditional_paths": [],
"frozen": False,
"outputs": [],
"field_order": [],
"beta": False,
"error": "string",
"edited": False,
"metadata": {},
},
}
response = await client.post("api/v1/validate/prompt", json=basic_case)
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert isinstance(result, dict), "The result must be a dictionary"
assert "frontend_node" in result, "The result must have a 'frontend_node' key"
assert "input_variables" in result, "The result must have an 'input_variables' key"

View file

@ -43,6 +43,7 @@ AI: """
return graph return graph
@pytest.mark.usefixtures("client")
def test_memory_chatbot(memory_chatbot_graph): def test_memory_chatbot(memory_chatbot_graph):
# Now we run step by step # Now we run step by step
expected_order = deque(["chat_input", "chat_memory", "prompt", "openai", "chat_output"]) expected_order = deque(["chat_input", "chat_memory", "prompt", "openai", "chat_output"])

View file

@ -4,10 +4,10 @@ from uuid import UUID, uuid4
import orjson import orjson
import pytest import pytest
from fastapi.testclient import TestClient from httpx import AsyncClient
from langflow.api.v1.schemas import FlowListCreate, ResultDataResponse from langflow.api.v1.schemas import FlowListCreate, ResultDataResponse
from langflow.graph.utils import log_transaction, log_vertex_build from langflow.graph.utils import log_transaction, log_vertex_build
from langflow.initial_setup.setup import load_flows_from_directory, load_starter_projects from langflow.initial_setup.setup import load_starter_projects
from langflow.services.database.models.base import orjson_dumps from langflow.services.database.models.base import orjson_dumps
from langflow.services.database.models.flow import Flow, FlowCreate, FlowUpdate from langflow.services.database.models.flow import Flow, FlowCreate, FlowUpdate
from langflow.services.database.models.folder.model import FolderCreate from langflow.services.database.models.folder.model import FolderCreate
@ -30,7 +30,7 @@ def json_style():
@pytest.mark.usefixtures("active_user") @pytest.mark.usefixtures("active_user")
async def test_create_flow(client: TestClient, json_flow: str, logged_in_headers): async def test_create_flow(client: AsyncClient, json_flow: str, logged_in_headers):
flow = orjson.loads(json_flow) flow = orjson.loads(json_flow)
data = flow["data"] data = flow["data"]
flow = FlowCreate(name=str(uuid4()), description="description", data=data) flow = FlowCreate(name=str(uuid4()), description="description", data=data)
@ -47,7 +47,7 @@ async def test_create_flow(client: TestClient, json_flow: str, logged_in_headers
@pytest.mark.usefixtures("active_user") @pytest.mark.usefixtures("active_user")
async def test_read_flows(client: TestClient, json_flow: str, logged_in_headers): async def test_read_flows(client: AsyncClient, json_flow: str, logged_in_headers):
flow_data = orjson.loads(json_flow) flow_data = orjson.loads(json_flow)
data = flow_data["data"] data = flow_data["data"]
flow = FlowCreate(name=str(uuid4()), description="description", data=data) flow = FlowCreate(name=str(uuid4()), description="description", data=data)
@ -67,14 +67,7 @@ async def test_read_flows(client: TestClient, json_flow: str, logged_in_headers)
assert len(response.json()) > 0 assert len(response.json()) > 0
async def test_read_flows_pagination_without_params(client: TestClient, logged_in_headers): async def test_read_flows_pagination_with_params(client: AsyncClient, logged_in_headers):
response = await client.get("api/v1/flows/", headers=logged_in_headers)
response_json = response.json()
assert response.status_code == 200
assert len(response_json) == 0
async def test_read_flows_pagination_with_params(client: TestClient, logged_in_headers):
response = await client.get( response = await client.get(
"api/v1/flows/", headers=logged_in_headers, params={"page": 3, "size": 10, "get_all": False} "api/v1/flows/", headers=logged_in_headers, params={"page": 3, "size": 10, "get_all": False}
) )
@ -86,7 +79,7 @@ async def test_read_flows_pagination_with_params(client: TestClient, logged_in_h
assert len(response.json()["items"]) == 0 assert len(response.json()["items"]) == 0
async def test_read_flows_pagination_with_flows(client: TestClient, logged_in_headers): async def test_read_flows_pagination_with_flows(client: AsyncClient, logged_in_headers):
number_of_flows = 30 number_of_flows = 30
flows = [FlowCreate(name=f"Flow {i}", description="description", data={}) for i in range(number_of_flows)] flows = [FlowCreate(name=f"Flow {i}", description="description", data={}) for i in range(number_of_flows)]
flow_ids = [] flow_ids = []
@ -116,7 +109,7 @@ async def test_read_flows_pagination_with_flows(client: TestClient, logged_in_he
assert len(response.json()["items"]) == 0 assert len(response.json()["items"]) == 0
async def test_read_flows_custom_page_size(client: TestClient, logged_in_headers): async def test_read_flows_custom_page_size(client: AsyncClient, logged_in_headers):
number_of_flows = 30 number_of_flows = 30
flows = [FlowCreate(name=f"Flow {i}", description="description", data={}) for i in range(number_of_flows)] flows = [FlowCreate(name=f"Flow {i}", description="description", data={}) for i in range(number_of_flows)]
for flow in flows: for flow in flows:
@ -134,7 +127,7 @@ async def test_read_flows_custom_page_size(client: TestClient, logged_in_headers
assert len(response.json()["items"]) == 15 assert len(response.json()["items"]) == 15
async def test_read_flows_invalid_page(client: TestClient, logged_in_headers): async def test_read_flows_invalid_page(client: AsyncClient, logged_in_headers):
number_of_flows = 30 number_of_flows = 30
flows = [FlowCreate(name=f"Flow {i}", description="description", data={}) for i in range(number_of_flows)] flows = [FlowCreate(name=f"Flow {i}", description="description", data={}) for i in range(number_of_flows)]
flow_ids = [] flow_ids = []
@ -149,7 +142,7 @@ async def test_read_flows_invalid_page(client: TestClient, logged_in_headers):
assert response.status_code == 422 # Assuming 422 is the status code for invalid input assert response.status_code == 422 # Assuming 422 is the status code for invalid input
async def test_read_flows_invalid_size(client: TestClient, logged_in_headers): async def test_read_flows_invalid_size(client: AsyncClient, logged_in_headers):
number_of_flows = 30 number_of_flows = 30
flows = [FlowCreate(name=f"Flow {i}", description="description", data={}) for i in range(number_of_flows)] flows = [FlowCreate(name=f"Flow {i}", description="description", data={}) for i in range(number_of_flows)]
flow_ids = [] flow_ids = []
@ -164,7 +157,7 @@ async def test_read_flows_invalid_size(client: TestClient, logged_in_headers):
assert response.status_code == 422 # Assuming 422 is the status code for invalid input assert response.status_code == 422 # Assuming 422 is the status code for invalid input
async def test_read_flows_no_pagination_params(client: TestClient, logged_in_headers): async def test_read_flows_no_pagination_params(client: AsyncClient, logged_in_headers):
number_of_flows = 30 number_of_flows = 30
flows = [FlowCreate(name=f"Flow {i}", description="description", data={}) for i in range(number_of_flows)] flows = [FlowCreate(name=f"Flow {i}", description="description", data={}) for i in range(number_of_flows)]
for flow in flows: for flow in flows:
@ -181,7 +174,7 @@ async def test_read_flows_no_pagination_params(client: TestClient, logged_in_hea
assert len(response.json()["items"]) == number_of_flows assert len(response.json()["items"]) == number_of_flows
async def test_read_flows_components_only_paginated(client: TestClient, logged_in_headers): async def test_read_flows_components_only_paginated(client: AsyncClient, logged_in_headers):
number_of_flows = 10 number_of_flows = 10
flows = [ flows = [
FlowCreate(name=f"Flow {i}", description="description", data={}, is_component=True) FlowCreate(name=f"Flow {i}", description="description", data={}, is_component=True)
@ -202,7 +195,7 @@ async def test_read_flows_components_only_paginated(client: TestClient, logged_i
assert all(flow["is_component"] is True for flow in response_json["items"]) assert all(flow["is_component"] is True for flow in response_json["items"])
async def test_read_flows_components_only(client: TestClient, logged_in_headers): async def test_read_flows_components_only(client: AsyncClient, logged_in_headers):
number_of_flows = 10 number_of_flows = 10
flows = [ flows = [
FlowCreate(name=f"Flow {i}", description="description", data={}, is_component=True) FlowCreate(name=f"Flow {i}", description="description", data={}, is_component=True)
@ -217,7 +210,7 @@ async def test_read_flows_components_only(client: TestClient, logged_in_headers)
assert all(flow["is_component"] is True for flow in response_json) assert all(flow["is_component"] is True for flow in response_json)
async def test_read_flow(client: TestClient, json_flow: str, logged_in_headers): async def test_read_flow(client: AsyncClient, json_flow: str, logged_in_headers):
flow = orjson.loads(json_flow) flow = orjson.loads(json_flow)
data = flow["data"] data = flow["data"]
unique_name = str(uuid4()) unique_name = str(uuid4())
@ -234,7 +227,7 @@ async def test_read_flow(client: TestClient, json_flow: str, logged_in_headers):
@pytest.mark.usefixtures("active_user") @pytest.mark.usefixtures("active_user")
async def test_update_flow(client: TestClient, json_flow: str, logged_in_headers): async def test_update_flow(client: AsyncClient, json_flow: str, logged_in_headers):
flow = orjson.loads(json_flow) flow = orjson.loads(json_flow)
data = flow["data"] data = flow["data"]
@ -256,7 +249,7 @@ async def test_update_flow(client: TestClient, json_flow: str, logged_in_headers
@pytest.mark.usefixtures("active_user") @pytest.mark.usefixtures("active_user")
async def test_delete_flow(client: TestClient, json_flow: str, logged_in_headers): async def test_delete_flow(client: AsyncClient, json_flow: str, logged_in_headers):
flow = orjson.loads(json_flow) flow = orjson.loads(json_flow)
data = flow["data"] data = flow["data"]
flow = FlowCreate(name="Test Flow", description="description", data=data) flow = FlowCreate(name="Test Flow", description="description", data=data)
@ -268,7 +261,7 @@ async def test_delete_flow(client: TestClient, json_flow: str, logged_in_headers
@pytest.mark.usefixtures("active_user") @pytest.mark.usefixtures("active_user")
async def test_delete_flows(client: TestClient, logged_in_headers): async def test_delete_flows(client: AsyncClient, logged_in_headers):
# Create ten flows # Create ten flows
number_of_flows = 10 number_of_flows = 10
flows = [FlowCreate(name=f"Flow {i}", description="description", data={}) for i in range(number_of_flows)] flows = [FlowCreate(name=f"Flow {i}", description="description", data={}) for i in range(number_of_flows)]
@ -285,7 +278,7 @@ async def test_delete_flows(client: TestClient, logged_in_headers):
@pytest.mark.asyncio @pytest.mark.asyncio
@pytest.mark.usefixtures("active_user") @pytest.mark.usefixtures("active_user")
async def test_delete_flows_with_transaction_and_build(client: TestClient, logged_in_headers): async def test_delete_flows_with_transaction_and_build(client: AsyncClient, logged_in_headers):
# Create ten flows # Create ten flows
number_of_flows = 10 number_of_flows = 10
flows = [FlowCreate(name=f"Flow {i}", description="description", data={}) for i in range(number_of_flows)] flows = [FlowCreate(name=f"Flow {i}", description="description", data={}) for i in range(number_of_flows)]
@ -344,7 +337,7 @@ async def test_delete_flows_with_transaction_and_build(client: TestClient, logge
@pytest.mark.asyncio @pytest.mark.asyncio
@pytest.mark.usefixtures("active_user") @pytest.mark.usefixtures("active_user")
async def test_delete_folder_with_flows_with_transaction_and_build(client: TestClient, logged_in_headers): async def test_delete_folder_with_flows_with_transaction_and_build(client: AsyncClient, logged_in_headers):
# Create a new folder # Create a new folder
folder_name = f"Test Folder {uuid4()}" folder_name = f"Test Folder {uuid4()}"
folder = FolderCreate(name=folder_name, description="Test folder description", components_list=[], flows_list=[]) folder = FolderCreate(name=folder_name, description="Test folder description", components_list=[], flows_list=[])
@ -411,7 +404,7 @@ async def test_delete_folder_with_flows_with_transaction_and_build(client: TestC
assert response.json() == {"vertex_builds": {}} assert response.json() == {"vertex_builds": {}}
async def test_get_flows_from_folder_pagination(client: TestClient, logged_in_headers): async def test_get_flows_from_folder_pagination(client: AsyncClient, logged_in_headers):
# Create a new folder # Create a new folder
folder_name = f"Test Folder {uuid4()}" folder_name = f"Test Folder {uuid4()}"
folder = FolderCreate(name=folder_name, description="Test folder description", components_list=[], flows_list=[]) folder = FolderCreate(name=folder_name, description="Test folder description", components_list=[], flows_list=[])
@ -435,7 +428,7 @@ async def test_get_flows_from_folder_pagination(client: TestClient, logged_in_he
assert len(response.json()["flows"]["items"]) == 0 assert len(response.json()["flows"]["items"]) == 0
async def test_get_flows_from_folder_pagination_with_params(client: TestClient, logged_in_headers): async def test_get_flows_from_folder_pagination_with_params(client: AsyncClient, logged_in_headers):
# Create a new folder # Create a new folder
folder_name = f"Test Folder {uuid4()}" folder_name = f"Test Folder {uuid4()}"
folder = FolderCreate(name=folder_name, description="Test folder description", components_list=[], flows_list=[]) folder = FolderCreate(name=folder_name, description="Test folder description", components_list=[], flows_list=[])
@ -460,7 +453,7 @@ async def test_get_flows_from_folder_pagination_with_params(client: TestClient,
@pytest.mark.usefixtures("session") @pytest.mark.usefixtures("session")
async def test_create_flows(client: TestClient, json_flow: str, logged_in_headers): async def test_create_flows(client: AsyncClient, json_flow: str, logged_in_headers):
flow = orjson.loads(json_flow) flow = orjson.loads(json_flow)
data = flow["data"] data = flow["data"]
# Create test data # Create test data
@ -488,7 +481,7 @@ async def test_create_flows(client: TestClient, json_flow: str, logged_in_header
@pytest.mark.usefixtures("session") @pytest.mark.usefixtures("session")
async def test_upload_file(client: TestClient, json_flow: str, logged_in_headers): async def test_upload_file(client: AsyncClient, json_flow: str, logged_in_headers):
flow = orjson.loads(json_flow) flow = orjson.loads(json_flow)
data = flow["data"] data = flow["data"]
# Create test data # Create test data
@ -521,7 +514,7 @@ async def test_upload_file(client: TestClient, json_flow: str, logged_in_headers
@pytest.mark.usefixtures("session") @pytest.mark.usefixtures("session")
async def test_download_file( async def test_download_file(
client: TestClient, client: AsyncClient,
json_flow, json_flow,
active_user, active_user,
logged_in_headers, logged_in_headers,
@ -563,21 +556,21 @@ async def test_download_file(
@pytest.mark.usefixtures("active_user") @pytest.mark.usefixtures("active_user")
async def test_create_flow_with_invalid_data(client: TestClient, logged_in_headers): async def test_create_flow_with_invalid_data(client: AsyncClient, logged_in_headers):
flow = {"name": "a" * 256, "data": "Invalid flow data"} flow = {"name": "a" * 256, "data": "Invalid flow data"}
response = await client.post("api/v1/flows/", json=flow, headers=logged_in_headers) response = await client.post("api/v1/flows/", json=flow, headers=logged_in_headers)
assert response.status_code == 422 assert response.status_code == 422
@pytest.mark.usefixtures("active_user") @pytest.mark.usefixtures("active_user")
async def test_get_nonexistent_flow(client: TestClient, logged_in_headers): async def test_get_nonexistent_flow(client: AsyncClient, logged_in_headers):
uuid = uuid4() uuid = uuid4()
response = await client.get(f"api/v1/flows/{uuid}", headers=logged_in_headers) response = await client.get(f"api/v1/flows/{uuid}", headers=logged_in_headers)
assert response.status_code == 404 assert response.status_code == 404
@pytest.mark.usefixtures("active_user") @pytest.mark.usefixtures("active_user")
async def test_update_flow_idempotency(client: TestClient, json_flow: str, logged_in_headers): async def test_update_flow_idempotency(client: AsyncClient, json_flow: str, logged_in_headers):
flow_data = orjson.loads(json_flow) flow_data = orjson.loads(json_flow)
data = flow_data["data"] data = flow_data["data"]
flow_data = FlowCreate(name="Test Flow", description="description", data=data) flow_data = FlowCreate(name="Test Flow", description="description", data=data)
@ -590,7 +583,7 @@ async def test_update_flow_idempotency(client: TestClient, json_flow: str, logge
@pytest.mark.usefixtures("active_user") @pytest.mark.usefixtures("active_user")
async def test_update_nonexistent_flow(client: TestClient, json_flow: str, logged_in_headers): async def test_update_nonexistent_flow(client: AsyncClient, json_flow: str, logged_in_headers):
flow_data = orjson.loads(json_flow) flow_data = orjson.loads(json_flow)
data = flow_data["data"] data = flow_data["data"]
uuid = uuid4() uuid = uuid4()
@ -604,34 +597,20 @@ async def test_update_nonexistent_flow(client: TestClient, json_flow: str, logge
@pytest.mark.usefixtures("active_user") @pytest.mark.usefixtures("active_user")
async def test_delete_nonexistent_flow(client: TestClient, logged_in_headers): async def test_delete_nonexistent_flow(client: AsyncClient, logged_in_headers):
uuid = uuid4() uuid = uuid4()
response = await client.delete(f"api/v1/flows/{uuid}", headers=logged_in_headers) response = await client.delete(f"api/v1/flows/{uuid}", headers=logged_in_headers)
assert response.status_code == 404 assert response.status_code == 404
@pytest.mark.usefixtures("active_user") @pytest.mark.usefixtures("active_user")
async def test_read_only_starter_projects(client: TestClient, logged_in_headers): async def test_read_only_starter_projects(client: AsyncClient, logged_in_headers):
response = await client.get("api/v1/flows/basic_examples/", headers=logged_in_headers) response = await client.get("api/v1/flows/basic_examples/", headers=logged_in_headers)
starter_projects = load_starter_projects() starter_projects = load_starter_projects()
assert response.status_code == 200 assert response.status_code == 200
assert len(response.json()) == len(starter_projects) assert len(response.json()) == len(starter_projects)
@pytest.mark.load_flows
async def test_load_flows(client: TestClient):
response = await client.get("api/v1/flows/c54f9130-f2fa-4a3e-b22a-3856d946351b")
assert response.status_code == 200
assert response.json()["name"] == "BasicExample"
assert response.json()["folder_id"] is not None
# re-run to ensure updates work well
load_flows_from_directory()
response = await client.get("api/v1/flows/c54f9130-f2fa-4a3e-b22a-3856d946351b")
assert response.status_code == 200
assert response.json()["name"] == "BasicExample"
assert response.json()["folder_id"] is not None
def test_sqlite_pragmas(): def test_sqlite_pragmas():
db_service = get_db_service() db_service = get_db_service()
@ -643,7 +622,7 @@ def test_sqlite_pragmas():
@pytest.mark.usefixtures("active_user") @pytest.mark.usefixtures("active_user")
async def test_read_folder(client: TestClient, logged_in_headers): async def test_read_folder(client: AsyncClient, logged_in_headers):
# Create a new folder # Create a new folder
folder_name = f"Test Folder {uuid4()}" folder_name = f"Test Folder {uuid4()}"
folder = FolderCreate(name=folder_name, description="Test folder description") folder = FolderCreate(name=folder_name, description="Test folder description")
@ -663,7 +642,7 @@ async def test_read_folder(client: TestClient, logged_in_headers):
@pytest.mark.usefixtures("active_user") @pytest.mark.usefixtures("active_user")
async def test_read_folder_with_pagination(client: TestClient, logged_in_headers): async def test_read_folder_with_pagination(client: AsyncClient, logged_in_headers):
# Create a new folder # Create a new folder
folder_name = f"Test Folder {uuid4()}" folder_name = f"Test Folder {uuid4()}"
folder = FolderCreate(name=folder_name, description="Test folder description") folder = FolderCreate(name=folder_name, description="Test folder description")
@ -689,7 +668,7 @@ async def test_read_folder_with_pagination(client: TestClient, logged_in_headers
@pytest.mark.usefixtures("active_user") @pytest.mark.usefixtures("active_user")
async def test_read_folder_with_flows(client: TestClient, json_flow: str, logged_in_headers): async def test_read_folder_with_flows(client: AsyncClient, json_flow: str, logged_in_headers):
# Create a new folder # Create a new folder
folder_name = f"Test Folder {uuid4()}" folder_name = f"Test Folder {uuid4()}"
flow_name = f"Test Flow {uuid4()}" flow_name = f"Test Flow {uuid4()}"
@ -718,7 +697,7 @@ async def test_read_folder_with_flows(client: TestClient, json_flow: str, logged
@pytest.mark.usefixtures("active_user") @pytest.mark.usefixtures("active_user")
async def test_read_nonexistent_folder(client: TestClient, logged_in_headers): async def test_read_nonexistent_folder(client: AsyncClient, logged_in_headers):
nonexistent_id = str(uuid4()) nonexistent_id = str(uuid4())
response = await client.get(f"api/v1/folders/{nonexistent_id}", headers=logged_in_headers) response = await client.get(f"api/v1/folders/{nonexistent_id}", headers=logged_in_headers)
assert response.status_code == 404 assert response.status_code == 404
@ -726,7 +705,7 @@ async def test_read_nonexistent_folder(client: TestClient, logged_in_headers):
@pytest.mark.usefixtures("active_user") @pytest.mark.usefixtures("active_user")
async def test_read_folder_with_search(client: TestClient, json_flow: str, logged_in_headers): async def test_read_folder_with_search(client: AsyncClient, json_flow: str, logged_in_headers):
# Create a new folder # Create a new folder
folder_name = f"Test Folder {uuid4()}" folder_name = f"Test Folder {uuid4()}"
folder = FolderCreate(name=folder_name, description="Test folder description") folder = FolderCreate(name=folder_name, description="Test folder description")
@ -762,7 +741,7 @@ async def test_read_folder_with_search(client: TestClient, json_flow: str, logge
@pytest.mark.usefixtures("active_user") @pytest.mark.usefixtures("active_user")
async def test_read_folder_with_component_filter(client: TestClient, json_flow: str, logged_in_headers): async def test_read_folder_with_component_filter(client: AsyncClient, json_flow: str, logged_in_headers):
# Create a new folder # Create a new folder
folder_name = f"Test Folder {uuid4()}" folder_name = f"Test Folder {uuid4()}"
folder = FolderCreate(name=folder_name, description="Test folder description") folder = FolderCreate(name=folder_name, description="Test folder description")

View file

@ -108,157 +108,6 @@ PROMPT_REQUEST = {
} }
# def test_process_flow_invalid_api_key(client, flow, monkeypatch):
# # Mock de process_graph_cached
# from langflow.api.v1 import endpoints
# from langflow.services.database.models.api_key import crud
# settings_service = get_settings_service()
# settings_service.auth_settings.AUTO_LOGIN = False
# async def mock_process_graph_cached(*args, **kwargs):
# return Result(result={}, session_id="session_id_mock")
# def mock_update_total_uses(*args, **kwargs):
# return created_api_key
# monkeypatch.setattr(endpoints, "process_graph_cached", mock_process_graph_cached)
# monkeypatch.setattr(crud, "update_total_uses", mock_update_total_uses)
# headers = {"x-api-key": "invalid_api_key"}
# post_data = {
# "inputs": {"key": "value"},
# "tweaks": None,
# "clear_cache": False,
# "session_id": None,
# }
# response = await client.post(f"api/v1/process/{flow.id}", headers=headers, json=post_data)
# assert response.status_code == 403
# assert response.json() == {"detail": "Invalid or missing API key"}
# def test_process_flow_invalid_id(client, monkeypatch, created_api_key):
# async def mock_process_graph_cached(*args, **kwargs):
# return Result(result={}, session_id="session_id_mock")
# from langflow.api.v1 import endpoints
# monkeypatch.setattr(endpoints, "process_graph_cached", mock_process_graph_cached)
# api_key = created_api_key.api_key
# headers = {"x-api-key": api_key}
# post_data = {
# "inputs": {"key": "value"},
# "tweaks": None,
# "clear_cache": False,
# "session_id": None,
# }
# invalid_id = uuid.uuid4()
# response = await client.post(f"api/v1/process/{invalid_id}", headers=headers, json=post_data)
# assert response.status_code == 404
# assert f"Flow {invalid_id} not found" in response.json()["detail"]
# def test_process_flow_without_autologin(client, flow, monkeypatch, created_api_key):
# # Mock de process_graph_cached
# from langflow.api.v1 import endpoints
# from langflow.services.database.models.api_key import crud
# settings_service = get_settings_service()
# settings_service.auth_settings.AUTO_LOGIN = False
# async def mock_process_graph_cached(*args, **kwargs):
# return Result(result={}, session_id="session_id_mock")
# def mock_process_graph_cached_task(*args, **kwargs):
# return Result(result={}, session_id="session_id_mock")
# # The task function is ran like this:
# # if not self.use_celery:
# # return None, await task_func(*args, **kwargs)
# # if not hasattr(task_func, "apply"):
# # raise ValueError(f"Task function {task_func} does not have an apply method")
# # task = task_func.apply(args=args, kwargs=kwargs)
# # result = task.get()
# # return task.id, result
# # So we need to mock the task function to return a task object
# # and then mock the task object to return a result
# # maybe a named tuple would be better here
# task = namedtuple("task", ["id", "get"])
# mock_process_graph_cached_task.apply = lambda *args, **kwargs: task(
# id="task_id_mock", get=lambda: Result(result={}, session_id="session_id_mock")
# )
# def mock_update_total_uses(*args, **kwargs):
# return created_api_key
# monkeypatch.setattr(endpoints, "process_graph_cached", mock_process_graph_cached)
# monkeypatch.setattr(crud, "update_total_uses", mock_update_total_uses)
# monkeypatch.setattr(endpoints, "process_graph_cached_task", mock_process_graph_cached_task)
# api_key = created_api_key.api_key
# headers = {"x-api-key": api_key}
# # Dummy POST data
# post_data = {
# "inputs": {"input": "value"},
# "tweaks": None,
# "clear_cache": False,
# "session_id": None,
# }
# # Make the request to the FastAPI TestClient
# response = await client.post(f"api/v1/process/{flow.id}", headers=headers, json=post_data)
# # Check the response
# assert response.status_code == 200, response.json()
# assert response.json()["result"] == {}, response.json()
# assert response.json()["session_id"] == "session_id_mock", response.json()
# def test_process_flow_fails_autologin_off(client, flow, monkeypatch):
# # Mock de process_graph_cached
# from langflow.api.v1 import endpoints
# from langflow.services.database.models.api_key import crud
# settings_service = get_settings_service()
# settings_service.auth_settings.AUTO_LOGIN = False
# async def mock_process_graph_cached(*args, **kwargs):
# return Result(result={}, session_id="session_id_mock")
# async def mock_update_total_uses(*args, **kwargs):
# return created_api_key
# monkeypatch.setattr(endpoints, "process_graph_cached", mock_process_graph_cached)
# monkeypatch.setattr(crud, "update_total_uses", mock_update_total_uses)
# headers = {"x-api-key": "api_key"}
# # Dummy POST data
# post_data = {
# "inputs": {"key": "value"},
# "tweaks": None,
# "clear_cache": False,
# "session_id": None,
# }
# # Make the request to the FastAPI TestClient
# response = await client.post(f"api/v1/process/{flow.id}", headers=headers, json=post_data)
# # Check the response
# assert response.status_code == 403, response.json()
# assert response.json() == {"detail": "Invalid or missing API key"}
async def test_get_all(client: AsyncClient, logged_in_headers): async def test_get_all(client: AsyncClient, logged_in_headers):
response = await client.get("api/v1/all", headers=logged_in_headers) response = await client.get("api/v1/all", headers=logged_in_headers)
assert response.status_code == 200 assert response.status_code == 200

View file

@ -159,20 +159,6 @@ async def test_add_user(test_user):
assert test_user["username"] == "testuser" assert test_user["username"] == "testuser"
# This is not used in the Frontend at the moment
# 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 = await client.post("api/v1/login", data=login_data)
# assert response.status_code == 200
# headers = {"Authorization": f"Bearer {response.json()['access_token']}"}
# response = await client.get("api/v1/user", headers=headers)
# assert response.status_code == 200, response.json()
# assert response.json()["username"] == "testuser"
@pytest.mark.api_key_required @pytest.mark.api_key_required
async def test_read_all_users(client: AsyncClient, super_user_headers): async def test_read_all_users(client: AsyncClient, super_user_headers):
response = await client.get("api/v1/users/", headers=super_user_headers) response = await client.get("api/v1/users/", headers=super_user_headers)