test: add unit tests for routes (#4249)
This commit is contained in:
parent
a88fd9bbb0
commit
fadb20115d
23 changed files with 623 additions and 272 deletions
|
|
@ -19,7 +19,7 @@ router = APIRouter(tags=["APIKey"], prefix="/api_key")
|
|||
|
||||
|
||||
@router.get("/")
|
||||
def get_api_keys_route(
|
||||
async def get_api_keys_route(
|
||||
db: DbSession,
|
||||
current_user: CurrentActiveUser,
|
||||
) -> ApiKeysResponse:
|
||||
|
|
@ -33,7 +33,7 @@ def get_api_keys_route(
|
|||
|
||||
|
||||
@router.post("/")
|
||||
def create_api_key_route(
|
||||
async def create_api_key_route(
|
||||
req: ApiKeyCreate,
|
||||
current_user: CurrentActiveUser,
|
||||
db: DbSession,
|
||||
|
|
@ -46,7 +46,7 @@ def create_api_key_route(
|
|||
|
||||
|
||||
@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,
|
||||
db: DbSession,
|
||||
):
|
||||
|
|
@ -58,7 +58,7 @@ def delete_api_key_route(
|
|||
|
||||
|
||||
@router.post("/store")
|
||||
def save_store_api_key(
|
||||
async def save_store_api_key(
|
||||
api_key_request: ApiKeyCreateRequest,
|
||||
response: Response,
|
||||
current_user: CurrentActiveUser,
|
||||
|
|
@ -90,17 +90,3 @@ def save_store_api_key(
|
|||
raise HTTPException(status_code=400, detail=str(e)) from e
|
||||
|
||||
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"}
|
||||
|
|
|
|||
|
|
@ -549,7 +549,7 @@ async def create_upload_file(
|
|||
|
||||
# get endpoint to return version of langflow
|
||||
@router.get("/version")
|
||||
def get_version():
|
||||
async def get_version():
|
||||
return get_version_info()
|
||||
|
||||
|
||||
|
|
@ -625,7 +625,7 @@ async def custom_component_update(
|
|||
|
||||
|
||||
@router.get("/config", response_model=ConfigResponse)
|
||||
def get_config():
|
||||
async def get_config():
|
||||
try:
|
||||
from langflow.services.deps import get_settings_service
|
||||
|
||||
|
|
@ -637,5 +637,5 @@ def get_config():
|
|||
|
||||
|
||||
@router.get("/sidebar_categories")
|
||||
def get_sidebar_categories() -> SidebarCategoriesResponse:
|
||||
async def get_sidebar_categories() -> SidebarCategoriesResponse:
|
||||
return SidebarCategoriesResponse(categories=SIDEBAR_CATEGORIES)
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ router = APIRouter(prefix="/flows", tags=["Flows"])
|
|||
|
||||
|
||||
@router.post("/", response_model=FlowRead, status_code=201)
|
||||
def create_flow(
|
||||
async def create_flow(
|
||||
*,
|
||||
session: DbSession,
|
||||
flow: FlowCreate,
|
||||
|
|
@ -124,7 +124,7 @@ def create_flow(
|
|||
|
||||
|
||||
@router.get("/", response_model=list[FlowRead] | Page[FlowRead] | list[FlowHeader], status_code=200)
|
||||
def read_flows(
|
||||
async def read_flows(
|
||||
*,
|
||||
current_user: CurrentActiveUser,
|
||||
session: DbSession,
|
||||
|
|
@ -226,7 +226,7 @@ def _read_flow(
|
|||
|
||||
|
||||
@router.get("/{flow_id}", response_model=FlowRead, status_code=200)
|
||||
def read_flow(
|
||||
async def read_flow(
|
||||
*,
|
||||
session: DbSession,
|
||||
flow_id: UUID,
|
||||
|
|
@ -239,7 +239,7 @@ def read_flow(
|
|||
|
||||
|
||||
@router.patch("/{flow_id}", response_model=FlowRead, status_code=200)
|
||||
def update_flow(
|
||||
async def update_flow(
|
||||
*,
|
||||
session: DbSession,
|
||||
flow_id: UUID,
|
||||
|
|
@ -320,7 +320,7 @@ async def delete_flow(
|
|||
|
||||
|
||||
@router.post("/batch/", response_model=list[FlowRead], status_code=201)
|
||||
def create_flows(
|
||||
async def create_flows(
|
||||
*,
|
||||
session: DbSession,
|
||||
flow_list: FlowListCreate,
|
||||
|
|
@ -357,7 +357,7 @@ async def upload_file(
|
|||
flow.user_id = current_user.id
|
||||
if 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)
|
||||
|
||||
return response_list
|
||||
|
|
@ -442,7 +442,7 @@ async def download_multiple_file(
|
|||
|
||||
|
||||
@router.get("/basic_examples/", response_model=list[FlowRead], status_code=200)
|
||||
def read_basic_examples(
|
||||
async def read_basic_examples(
|
||||
*,
|
||||
session: DbSession,
|
||||
):
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ router = APIRouter(prefix="/folders", tags=["Folders"])
|
|||
|
||||
|
||||
@router.post("/", response_model=FolderRead, status_code=201)
|
||||
def create_folder(
|
||||
async def create_folder(
|
||||
*,
|
||||
session: DbSession,
|
||||
folder: FolderCreate,
|
||||
|
|
@ -82,7 +82,7 @@ def create_folder(
|
|||
|
||||
|
||||
@router.get("/", response_model=list[FolderRead], status_code=200)
|
||||
def read_folders(
|
||||
async def read_folders(
|
||||
*,
|
||||
session: DbSession,
|
||||
current_user: CurrentActiveUser,
|
||||
|
|
@ -100,7 +100,7 @@ def read_folders(
|
|||
|
||||
|
||||
@router.get("/{folder_id}", response_model=FolderWithPaginatedFlows | FolderReadWithFlows, status_code=200)
|
||||
def read_folder(
|
||||
async def read_folder(
|
||||
*,
|
||||
session: DbSession,
|
||||
folder_id: str,
|
||||
|
|
@ -145,7 +145,7 @@ def read_folder(
|
|||
|
||||
|
||||
@router.patch("/{folder_id}", response_model=FolderRead, status_code=200)
|
||||
def update_folder(
|
||||
async def update_folder(
|
||||
*,
|
||||
session: DbSession,
|
||||
folder_id: str,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ router = APIRouter(prefix="/starter-projects", tags=["Flows"])
|
|||
|
||||
|
||||
@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."""
|
||||
from langflow.initial_setup.load import get_starter_projects_dump
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ def get_optional_user_store_api_key(user: CurrentActiveUser):
|
|||
|
||||
|
||||
@router.get("/check/")
|
||||
def check_if_store_is_enabled():
|
||||
async def check_if_store_is_enabled():
|
||||
return {
|
||||
"enabled": get_settings_service().settings.store,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ router = APIRouter(tags=["Users"], prefix="/users")
|
|||
|
||||
|
||||
@router.post("/", response_model=UserRead, status_code=201)
|
||||
def add_user(
|
||||
async def add_user(
|
||||
user: UserCreate,
|
||||
session: DbSession,
|
||||
) -> User:
|
||||
|
|
@ -46,7 +46,7 @@ def add_user(
|
|||
|
||||
|
||||
@router.get("/whoami", response_model=UserRead)
|
||||
def read_current_user(
|
||||
async def read_current_user(
|
||||
current_user: CurrentActiveUser,
|
||||
) -> User:
|
||||
"""Retrieve the current user's data."""
|
||||
|
|
@ -54,7 +54,7 @@ def read_current_user(
|
|||
|
||||
|
||||
@router.get("/", dependencies=[Depends(get_current_active_superuser)])
|
||||
def read_all_users(
|
||||
async def read_all_users(
|
||||
*,
|
||||
skip: int = 0,
|
||||
limit: int = 10,
|
||||
|
|
@ -74,7 +74,7 @@ def read_all_users(
|
|||
|
||||
|
||||
@router.patch("/{user_id}", response_model=UserRead)
|
||||
def patch_user(
|
||||
async def patch_user(
|
||||
user_id: UUID,
|
||||
user_update: UserUpdate,
|
||||
user: CurrentActiveUser,
|
||||
|
|
@ -101,7 +101,7 @@ def patch_user(
|
|||
|
||||
|
||||
@router.patch("/{user_id}/reset-password", response_model=UserRead)
|
||||
def reset_password(
|
||||
async def reset_password(
|
||||
user_id: UUID,
|
||||
user_update: UserUpdate,
|
||||
user: CurrentActiveUser,
|
||||
|
|
@ -124,7 +124,7 @@ def reset_password(
|
|||
|
||||
|
||||
@router.delete("/{user_id}")
|
||||
def delete_user(
|
||||
async def delete_user(
|
||||
user_id: UUID,
|
||||
current_user: Annotated[User, Depends(get_current_active_superuser)],
|
||||
session: DbSession,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ router = APIRouter(prefix="/validate", tags=["Validate"])
|
|||
|
||||
|
||||
@router.post("/code", status_code=200)
|
||||
def post_validate_code(code: Code) -> CodeValidationResponse:
|
||||
async def post_validate_code(code: Code) -> CodeValidationResponse:
|
||||
try:
|
||||
errors = validate_code(code.code)
|
||||
return CodeValidationResponse(
|
||||
|
|
@ -23,7 +23,7 @@ def post_validate_code(code: Code) -> CodeValidationResponse:
|
|||
|
||||
|
||||
@router.post("/prompt", status_code=200)
|
||||
def post_validate_prompt(prompt_request: ValidatePromptRequest) -> PromptValidationResponse:
|
||||
async def post_validate_prompt(prompt_request: ValidatePromptRequest) -> PromptValidationResponse:
|
||||
try:
|
||||
if not prompt_request.frontend_node:
|
||||
return PromptValidationResponse(
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ router = APIRouter(prefix="/variables", tags=["Variables"])
|
|||
|
||||
|
||||
@router.post("/", response_model=VariableRead, status_code=201)
|
||||
def create_variable(
|
||||
async def create_variable(
|
||||
*,
|
||||
session: DbSession,
|
||||
variable: VariableCreate,
|
||||
|
|
@ -48,7 +48,7 @@ def create_variable(
|
|||
|
||||
|
||||
@router.get("/", response_model=list[VariableRead], status_code=200)
|
||||
def read_variables(
|
||||
async def read_variables(
|
||||
*,
|
||||
session: DbSession,
|
||||
current_user: CurrentActiveUser,
|
||||
|
|
@ -65,7 +65,7 @@ def read_variables(
|
|||
|
||||
|
||||
@router.patch("/{variable_id}", response_model=VariableRead, status_code=200)
|
||||
def update_variable(
|
||||
async def update_variable(
|
||||
*,
|
||||
session: DbSession,
|
||||
variable_id: UUID,
|
||||
|
|
@ -92,7 +92,7 @@ def update_variable(
|
|||
|
||||
|
||||
@router.delete("/{variable_id}", status_code=204)
|
||||
def delete_variable(
|
||||
async def delete_variable(
|
||||
*,
|
||||
session: DbSession,
|
||||
variable_id: UUID,
|
||||
|
|
|
|||
|
|
@ -384,6 +384,44 @@ async def logged_in_headers(client, active_user):
|
|||
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
|
||||
def flow(
|
||||
client, # noqa: ARG001
|
||||
|
|
@ -484,7 +522,7 @@ async def added_webhook_test(client, json_webhook_test, logged_in_headers):
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
async def flow_component(client: TestClient, logged_in_headers):
|
||||
async def flow_component(client: AsyncClient, logged_in_headers):
|
||||
chat_input = ChatInput()
|
||||
graph = Graph(start=chat_input, end=chat_input)
|
||||
graph_dict = graph.dump(name="Chat Input Component")
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ from uuid import uuid4
|
|||
|
||||
import pytest
|
||||
from fastapi import status
|
||||
from fastapi.testclient import TestClient
|
||||
from httpx import AsyncClient
|
||||
from langflow.graph.schema import RunOutputs
|
||||
from langflow.initial_setup.setup import load_starter_projects
|
||||
from langflow.load import run_flow_from_json
|
||||
|
||||
|
||||
@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"]
|
||||
headers = {"x-api-key": created_api_key.api_key}
|
||||
payload = {
|
||||
|
|
@ -27,7 +27,7 @@ async def test_run_flow_with_caching_success(client: TestClient, starter_project
|
|||
|
||||
|
||||
@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()
|
||||
headers = {"x-api-key": created_api_key.api_key}
|
||||
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
|
||||
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"]
|
||||
headers = {"x-api-key": created_api_key.api_key}
|
||||
payload = {"input_value": {"key": "value"}, "input_type": "text", "output_type": "text", "tweaks": {}}
|
||||
|
|
|
|||
64
src/backend/tests/unit/api/v1/test_api_key.py
Normal file
64
src/backend/tests/unit/api/v1/test_api_key.py
Normal 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'"
|
||||
36
src/backend/tests/unit/api/v1/test_endpoints.py
Normal file
36
src/backend/tests/unit/api/v1/test_endpoints.py
Normal 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"
|
||||
172
src/backend/tests/unit/api/v1/test_flows.py
Normal file
172
src/backend/tests/unit/api/v1/test_flows.py
Normal 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"
|
||||
64
src/backend/tests/unit/api/v1/test_folders.py
Normal file
64
src/backend/tests/unit/api/v1/test_folders.py
Normal 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'"
|
||||
10
src/backend/tests/unit/api/v1/test_starter_projects.py
Normal file
10
src/backend/tests/unit/api/v1/test_starter_projects.py
Normal 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"
|
||||
12
src/backend/tests/unit/api/v1/test_store.py
Normal file
12
src/backend/tests/unit/api/v1/test_store.py
Normal 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"
|
||||
98
src/backend/tests/unit/api/v1/test_users.py
Normal file
98
src/backend/tests/unit/api/v1/test_users.py
Normal 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"
|
||||
56
src/backend/tests/unit/api/v1/test_validate.py
Normal file
56
src/backend/tests/unit/api/v1/test_validate.py
Normal 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"
|
||||
|
|
@ -43,6 +43,7 @@ AI: """
|
|||
return graph
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("client")
|
||||
def test_memory_chatbot(memory_chatbot_graph):
|
||||
# Now we run step by step
|
||||
expected_order = deque(["chat_input", "chat_memory", "prompt", "openai", "chat_output"])
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ from uuid import UUID, uuid4
|
|||
|
||||
import orjson
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from httpx import AsyncClient
|
||||
from langflow.api.v1.schemas import FlowListCreate, ResultDataResponse
|
||||
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.flow import Flow, FlowCreate, FlowUpdate
|
||||
from langflow.services.database.models.folder.model import FolderCreate
|
||||
|
|
@ -30,7 +30,7 @@ def json_style():
|
|||
|
||||
|
||||
@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)
|
||||
data = flow["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")
|
||||
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)
|
||||
data = flow_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
|
||||
|
||||
|
||||
async def test_read_flows_pagination_without_params(client: TestClient, 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):
|
||||
async def test_read_flows_pagination_with_params(client: AsyncClient, logged_in_headers):
|
||||
response = await client.get(
|
||||
"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
|
||||
|
||||
|
||||
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
|
||||
flows = [FlowCreate(name=f"Flow {i}", description="description", data={}) for i in range(number_of_flows)]
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
flows = [FlowCreate(name=f"Flow {i}", description="description", data={}) for i in range(number_of_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
|
||||
|
||||
|
||||
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
|
||||
flows = [FlowCreate(name=f"Flow {i}", description="description", data={}) for i in range(number_of_flows)]
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
flows = [FlowCreate(name=f"Flow {i}", description="description", data={}) for i in range(number_of_flows)]
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
flows = [FlowCreate(name=f"Flow {i}", description="description", data={}) for i in range(number_of_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
|
||||
|
||||
|
||||
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
|
||||
flows = [
|
||||
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"])
|
||||
|
||||
|
||||
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
|
||||
flows = [
|
||||
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)
|
||||
|
||||
|
||||
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)
|
||||
data = flow["data"]
|
||||
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")
|
||||
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)
|
||||
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")
|
||||
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)
|
||||
data = flow["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")
|
||||
async def test_delete_flows(client: TestClient, logged_in_headers):
|
||||
async def test_delete_flows(client: AsyncClient, logged_in_headers):
|
||||
# Create ten flows
|
||||
number_of_flows = 10
|
||||
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.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
|
||||
number_of_flows = 10
|
||||
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.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
|
||||
folder_name = f"Test Folder {uuid4()}"
|
||||
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": {}}
|
||||
|
||||
|
||||
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
|
||||
folder_name = f"Test Folder {uuid4()}"
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
folder_name = f"Test Folder {uuid4()}"
|
||||
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")
|
||||
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)
|
||||
data = flow["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")
|
||||
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)
|
||||
data = flow["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")
|
||||
async def test_download_file(
|
||||
client: TestClient,
|
||||
client: AsyncClient,
|
||||
json_flow,
|
||||
active_user,
|
||||
logged_in_headers,
|
||||
|
|
@ -563,21 +556,21 @@ async def test_download_file(
|
|||
|
||||
|
||||
@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"}
|
||||
response = await client.post("api/v1/flows/", json=flow, headers=logged_in_headers)
|
||||
assert response.status_code == 422
|
||||
|
||||
|
||||
@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()
|
||||
response = await client.get(f"api/v1/flows/{uuid}", headers=logged_in_headers)
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
@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)
|
||||
data = flow_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")
|
||||
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)
|
||||
data = flow_data["data"]
|
||||
uuid = uuid4()
|
||||
|
|
@ -604,34 +597,20 @@ async def test_update_nonexistent_flow(client: TestClient, json_flow: str, logge
|
|||
|
||||
|
||||
@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()
|
||||
response = await client.delete(f"api/v1/flows/{uuid}", headers=logged_in_headers)
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
@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)
|
||||
starter_projects = load_starter_projects()
|
||||
assert response.status_code == 200
|
||||
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():
|
||||
db_service = get_db_service()
|
||||
|
||||
|
|
@ -643,7 +622,7 @@ def test_sqlite_pragmas():
|
|||
|
||||
|
||||
@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
|
||||
folder_name = f"Test Folder {uuid4()}"
|
||||
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")
|
||||
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
|
||||
folder_name = f"Test Folder {uuid4()}"
|
||||
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")
|
||||
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
|
||||
folder_name = f"Test Folder {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")
|
||||
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())
|
||||
response = await client.get(f"api/v1/folders/{nonexistent_id}", headers=logged_in_headers)
|
||||
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")
|
||||
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
|
||||
folder_name = f"Test Folder {uuid4()}"
|
||||
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")
|
||||
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
|
||||
folder_name = f"Test Folder {uuid4()}"
|
||||
folder = FolderCreate(name=folder_name, description="Test folder description")
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
response = await client.get("api/v1/all", headers=logged_in_headers)
|
||||
assert response.status_code == 200
|
||||
|
|
|
|||
|
|
@ -159,20 +159,6 @@ async def test_add_user(test_user):
|
|||
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
|
||||
async def test_read_all_users(client: AsyncClient, super_user_headers):
|
||||
response = await client.get("api/v1/users/", headers=super_user_headers)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue