mirror of
https://github.com/langgenius/dify.git
synced 2026-02-09 15:10:13 -05:00
feat: use xdist to make make test faster (#30824)
Signed-off-by: yihong0618 <zouzou0208@gmail.com>
This commit is contained in:
1
.github/workflows/api-tests.yml
vendored
1
.github/workflows/api-tests.yml
vendored
@@ -72,6 +72,7 @@ jobs:
|
|||||||
OPENDAL_FS_ROOT: /tmp/dify-storage
|
OPENDAL_FS_ROOT: /tmp/dify-storage
|
||||||
run: |
|
run: |
|
||||||
uv run --project api pytest \
|
uv run --project api pytest \
|
||||||
|
-n auto \
|
||||||
--timeout "${PYTEST_TIMEOUT:-180}" \
|
--timeout "${PYTEST_TIMEOUT:-180}" \
|
||||||
api/tests/integration_tests/workflow \
|
api/tests/integration_tests/workflow \
|
||||||
api/tests/integration_tests/tools \
|
api/tests/integration_tests/tools \
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -80,7 +80,7 @@ test:
|
|||||||
echo "Target: $(TARGET_TESTS)"; \
|
echo "Target: $(TARGET_TESTS)"; \
|
||||||
uv run --project api --dev pytest $(TARGET_TESTS); \
|
uv run --project api --dev pytest $(TARGET_TESTS); \
|
||||||
else \
|
else \
|
||||||
uv run --project api --dev dev/pytest/pytest_unit_tests.sh; \
|
PYTEST_XDIST_ARGS="-n auto" uv run --project api --dev dev/pytest/pytest_unit_tests.sh; \
|
||||||
fi
|
fi
|
||||||
@echo "✅ Tests complete"
|
@echo "✅ Tests complete"
|
||||||
|
|
||||||
|
|||||||
@@ -175,6 +175,7 @@ dev = [
|
|||||||
# "locust>=2.40.4", # Temporarily removed due to compatibility issues. Uncomment when resolved.
|
# "locust>=2.40.4", # Temporarily removed due to compatibility issues. Uncomment when resolved.
|
||||||
"sseclient-py>=1.8.0",
|
"sseclient-py>=1.8.0",
|
||||||
"pytest-timeout>=2.4.0",
|
"pytest-timeout>=2.4.0",
|
||||||
|
"pytest-xdist>=3.8.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from unittest.mock import MagicMock, patch
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
|
||||||
# Getting the absolute path of the current file's directory
|
# Getting the absolute path of the current file's directory
|
||||||
ABS_PATH = os.path.dirname(os.path.abspath(__file__))
|
ABS_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||||
@@ -36,6 +37,7 @@ import sys
|
|||||||
|
|
||||||
sys.path.insert(0, PROJECT_DIR)
|
sys.path.insert(0, PROJECT_DIR)
|
||||||
|
|
||||||
|
from core.db.session_factory import configure_session_factory, session_factory
|
||||||
from extensions import ext_redis
|
from extensions import ext_redis
|
||||||
|
|
||||||
|
|
||||||
@@ -102,3 +104,18 @@ def reset_secret_key():
|
|||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
dify_config.SECRET_KEY = original
|
dify_config.SECRET_KEY = original
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def _unit_test_engine():
|
||||||
|
engine = create_engine("sqlite:///:memory:")
|
||||||
|
yield engine
|
||||||
|
engine.dispose()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def _configure_session_factory(_unit_test_engine):
|
||||||
|
try:
|
||||||
|
session_factory.get_session_maker()
|
||||||
|
except RuntimeError:
|
||||||
|
configure_session_factory(_unit_test_engine, expire_on_commit=False)
|
||||||
|
|||||||
@@ -31,6 +31,13 @@ def _load_app_module():
|
|||||||
|
|
||||||
def schema_model(self, name, schema):
|
def schema_model(self, name, schema):
|
||||||
self.models[name] = schema
|
self.models[name] = schema
|
||||||
|
return schema
|
||||||
|
|
||||||
|
def model(self, name, model_dict=None, **kwargs):
|
||||||
|
"""Register a model with the namespace (flask-restx compatibility)."""
|
||||||
|
if model_dict is not None:
|
||||||
|
self.models[name] = model_dict
|
||||||
|
return model_dict
|
||||||
|
|
||||||
def _decorator(self, obj):
|
def _decorator(self, obj):
|
||||||
return obj
|
return obj
|
||||||
|
|||||||
24
api/uv.lock
generated
24
api/uv.lock
generated
@@ -1479,6 +1479,7 @@ dev = [
|
|||||||
{ name = "pytest-env" },
|
{ name = "pytest-env" },
|
||||||
{ name = "pytest-mock" },
|
{ name = "pytest-mock" },
|
||||||
{ name = "pytest-timeout" },
|
{ name = "pytest-timeout" },
|
||||||
|
{ name = "pytest-xdist" },
|
||||||
{ name = "ruff" },
|
{ name = "ruff" },
|
||||||
{ name = "scipy-stubs" },
|
{ name = "scipy-stubs" },
|
||||||
{ name = "sseclient-py" },
|
{ name = "sseclient-py" },
|
||||||
@@ -1678,6 +1679,7 @@ dev = [
|
|||||||
{ name = "pytest-env", specifier = "~=1.1.3" },
|
{ name = "pytest-env", specifier = "~=1.1.3" },
|
||||||
{ name = "pytest-mock", specifier = "~=3.14.0" },
|
{ name = "pytest-mock", specifier = "~=3.14.0" },
|
||||||
{ name = "pytest-timeout", specifier = ">=2.4.0" },
|
{ name = "pytest-timeout", specifier = ">=2.4.0" },
|
||||||
|
{ name = "pytest-xdist", specifier = ">=3.8.0" },
|
||||||
{ name = "ruff", specifier = "~=0.14.0" },
|
{ name = "ruff", specifier = "~=0.14.0" },
|
||||||
{ name = "scipy-stubs", specifier = ">=1.15.3.0" },
|
{ name = "scipy-stubs", specifier = ">=1.15.3.0" },
|
||||||
{ name = "sseclient-py", specifier = ">=1.8.0" },
|
{ name = "sseclient-py", specifier = ">=1.8.0" },
|
||||||
@@ -1896,6 +1898,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/19/d8/2a1c638d9e0aa7e269269a1a1bf423ddd94267f1a01bbe3ad03432b67dd4/eval_type_backport-0.3.0-py3-none-any.whl", hash = "sha256:975a10a0fe333c8b6260d7fdb637698c9a16c3a9e3b6eb943fee6a6f67a37fe8", size = 6061, upload-time = "2025-11-13T20:56:49.499Z" },
|
{ url = "https://files.pythonhosted.org/packages/19/d8/2a1c638d9e0aa7e269269a1a1bf423ddd94267f1a01bbe3ad03432b67dd4/eval_type_backport-0.3.0-py3-none-any.whl", hash = "sha256:975a10a0fe333c8b6260d7fdb637698c9a16c3a9e3b6eb943fee6a6f67a37fe8", size = 6061, upload-time = "2025-11-13T20:56:49.499Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "execnet"
|
||||||
|
version = "2.1.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/bf/89/780e11f9588d9e7128a3f87788354c7946a9cbb1401ad38a48c4db9a4f07/execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd", size = 166622, upload-time = "2025-11-12T09:56:37.75Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec", size = 40708, upload-time = "2025-11-12T09:56:36.333Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "faker"
|
name = "faker"
|
||||||
version = "38.2.0"
|
version = "38.2.0"
|
||||||
@@ -5141,6 +5152,19 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", size = 14382, upload-time = "2025-05-05T19:44:33.502Z" },
|
{ url = "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", size = 14382, upload-time = "2025-05-05T19:44:33.502Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytest-xdist"
|
||||||
|
version = "3.8.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "execnet" },
|
||||||
|
{ name = "pytest" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/78/b4/439b179d1ff526791eb921115fca8e44e596a13efeda518b9d845a619450/pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1", size = 88069, upload-time = "2025-07-01T13:30:59.346Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88", size = 46396, upload-time = "2025-07-01T13:30:56.632Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-calamine"
|
name = "python-calamine"
|
||||||
version = "0.5.4"
|
version = "0.5.4"
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ SCRIPT_DIR="$(dirname "$(realpath "$0")")"
|
|||||||
cd "$SCRIPT_DIR/../.."
|
cd "$SCRIPT_DIR/../.."
|
||||||
|
|
||||||
PYTEST_TIMEOUT="${PYTEST_TIMEOUT:-20}"
|
PYTEST_TIMEOUT="${PYTEST_TIMEOUT:-20}"
|
||||||
|
PYTEST_XDIST_ARGS="${PYTEST_XDIST_ARGS:--n auto}"
|
||||||
|
|
||||||
# libs
|
# Run most tests in parallel (excluding controllers which have import conflicts with xdist)
|
||||||
pytest --timeout "${PYTEST_TIMEOUT}" api/tests/unit_tests
|
# Controller tests have module-level side effects (Flask route registration) that cause
|
||||||
|
# race conditions when imported concurrently by multiple pytest-xdist workers.
|
||||||
|
pytest --timeout "${PYTEST_TIMEOUT}" ${PYTEST_XDIST_ARGS} api/tests/unit_tests --ignore=api/tests/unit_tests/controllers
|
||||||
|
|
||||||
|
# Run controller tests sequentially to avoid import race conditions
|
||||||
|
pytest --timeout "${PYTEST_TIMEOUT}" api/tests/unit_tests/controllers
|
||||||
|
|||||||
Reference in New Issue
Block a user