Merge remote-tracking branch 'origin/main' into feat/support-agent-sandbox

# Conflicts:
#	api/.env.example
#	api/uv.lock
#	web/app/components/app/create-app-modal/index.tsx
#	web/app/components/app/create-from-dsl-modal/index.tsx
#	web/app/components/apps/app-card.tsx
#	web/pnpm-lock.yaml
This commit is contained in:
yyh
2026-01-29 21:25:28 +08:00
137 changed files with 5382 additions and 812 deletions

View File

@ -3,6 +3,7 @@ from unittest.mock import MagicMock, patch
import pytest
from flask import Flask
from sqlalchemy import create_engine
# Getting the absolute path of the current file's directory
ABS_PATH = os.path.dirname(os.path.abspath(__file__))
@ -36,6 +37,7 @@ import sys
sys.path.insert(0, PROJECT_DIR)
from core.db.session_factory import configure_session_factory, session_factory
from extensions import ext_redis
@ -102,3 +104,18 @@ def reset_secret_key():
yield
finally:
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)

View File

@ -31,6 +31,13 @@ def _load_app_module():
def schema_model(self, 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):
return obj

View File

@ -1,7 +1,9 @@
"""Primarily used for testing merged cell scenarios"""
import io
import os
import tempfile
from pathlib import Path
from types import SimpleNamespace
from docx import Document
@ -56,6 +58,42 @@ def test_parse_row():
assert extractor._parse_row(row, {}, 3) == gt[idx]
def test_init_downloads_via_ssrf_proxy(monkeypatch):
doc = Document()
doc.add_paragraph("hello")
buf = io.BytesIO()
doc.save(buf)
docx_bytes = buf.getvalue()
calls: list[tuple[str, object]] = []
class FakeResponse:
status_code = 200
content = docx_bytes
def close(self) -> None:
calls.append(("close", None))
def fake_get(url: str, **kwargs):
calls.append(("get", (url, kwargs)))
return FakeResponse()
monkeypatch.setattr(we, "ssrf_proxy", SimpleNamespace(get=fake_get))
extractor = WordExtractor("https://example.com/test.docx", "tenant_id", "user_id")
try:
assert calls
assert calls[0][0] == "get"
url, kwargs = calls[0][1]
assert url == "https://example.com/test.docx"
assert kwargs.get("timeout") is None
assert extractor.web_path == "https://example.com/test.docx"
assert extractor.file_path != extractor.web_path
assert Path(extractor.file_path).read_bytes() == docx_bytes
finally:
extractor.temp_file.close()
def test_extract_images_from_docx(monkeypatch):
external_bytes = b"ext-bytes"
internal_bytes = b"int-bytes"

View File

@ -138,6 +138,7 @@ class TestDatasetServiceUpdateDataset:
"services.dataset_service.DatasetCollectionBindingService.get_dataset_collection_binding"
) as mock_get_binding,
patch("services.dataset_service.deal_dataset_vector_index_task") as mock_task,
patch("services.dataset_service.regenerate_summary_index_task") as mock_regenerate_task,
patch(
"services.dataset_service.current_user", create_autospec(Account, instance=True)
) as mock_current_user,
@ -147,6 +148,7 @@ class TestDatasetServiceUpdateDataset:
"model_manager": mock_model_manager,
"get_binding": mock_get_binding,
"task": mock_task,
"regenerate_task": mock_regenerate_task,
"current_user": mock_current_user,
}
@ -549,6 +551,13 @@ class TestDatasetServiceUpdateDataset:
# Verify vector index task was triggered
mock_internal_provider_dependencies["task"].delay.assert_called_once_with("dataset-123", "update")
# Verify regenerate summary index task was triggered (when embedding_model changes)
mock_internal_provider_dependencies["regenerate_task"].delay.assert_called_once_with(
"dataset-123",
regenerate_reason="embedding_model_changed",
regenerate_vectors_only=True,
)
# Verify return value
assert result == dataset