Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5468c4ec96 | ||
|
|
f4c02e4c6b | ||
|
|
9dc95eeb20 | ||
|
|
76bba64b79 |
+50
-5
@@ -15,6 +15,7 @@ from openinference.semconv.trace import (
|
||||
SpanAttributes,
|
||||
ToolCallAttributes,
|
||||
)
|
||||
from opentelemetry.context import Context
|
||||
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter as GrpcOTLPSpanExporter
|
||||
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter as HttpOTLPSpanExporter
|
||||
from opentelemetry.sdk import trace as trace_sdk
|
||||
@@ -45,8 +46,8 @@ from dify_trace_arize_phoenix.config import ArizeConfig, PhoenixConfig
|
||||
from extensions.ext_database import db
|
||||
from extensions.ext_redis import redis_client
|
||||
from graphon.enums import WorkflowNodeExecutionStatus
|
||||
from models.model import EndUser, MessageFile
|
||||
from models.workflow import WorkflowNodeExecutionTriggeredFrom
|
||||
from models.model import App, EndUser, MessageFile
|
||||
from models.workflow import WorkflowNodeExecutionTriggeredFrom, WorkflowRun
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -139,6 +140,48 @@ def _resolve_published_parent_span_context(parent_node_execution_id: str) -> dic
|
||||
return normalized_carrier
|
||||
|
||||
|
||||
def _app_uses_phoenix_provider(app_tracing_config: Mapping[str, Any] | None) -> bool:
|
||||
if not app_tracing_config or not app_tracing_config.get("enabled"):
|
||||
return False
|
||||
return app_tracing_config.get("tracing_provider") in {"arize", "phoenix"}
|
||||
|
||||
|
||||
def _parent_workflow_can_publish_span_context(parent_workflow_run_id: str) -> bool:
|
||||
parent_run = db.session.query(WorkflowRun).where(WorkflowRun.id == parent_workflow_run_id).first()
|
||||
if parent_run is None:
|
||||
return True
|
||||
|
||||
parent_app = db.session.query(App).where(App.id == parent_run.app_id).first()
|
||||
if parent_app is None or not parent_app.tracing:
|
||||
return False
|
||||
|
||||
try:
|
||||
app_tracing_config = json.loads(parent_app.tracing)
|
||||
except (TypeError, json.JSONDecodeError):
|
||||
return False
|
||||
if not isinstance(app_tracing_config, Mapping):
|
||||
return False
|
||||
|
||||
return _app_uses_phoenix_provider(app_tracing_config)
|
||||
|
||||
|
||||
def _resolve_workflow_parent_carrier(
|
||||
parent_node_execution_id: str, parent_workflow_run_id: str | None
|
||||
) -> dict[str, str] | None:
|
||||
try:
|
||||
return _resolve_published_parent_span_context(parent_node_execution_id)
|
||||
except PendingTraceParentContextError:
|
||||
if parent_workflow_run_id and not _parent_workflow_can_publish_span_context(parent_workflow_run_id):
|
||||
logger.info(
|
||||
"[Arize/Phoenix] Parent workflow cannot publish Phoenix span context; falling back to root span: "
|
||||
"parent_workflow_run_id=%s parent_node_execution_id=%s",
|
||||
parent_workflow_run_id,
|
||||
parent_node_execution_id,
|
||||
)
|
||||
return None
|
||||
raise
|
||||
|
||||
|
||||
def setup_tracer(arize_phoenix_config: ArizeConfig | PhoenixConfig) -> tuple[trace_sdk.Tracer, SimpleSpanProcessor]:
|
||||
"""Configure OpenTelemetry tracer with OTLP exporter for Arize/Phoenix."""
|
||||
try:
|
||||
@@ -581,9 +624,11 @@ class ArizePhoenixDataTrace(BaseTraceInstance):
|
||||
workflow_session_id,
|
||||
)
|
||||
|
||||
workflow_parent_carrier: dict[str, str] | None = None
|
||||
if parent_node_execution_id:
|
||||
workflow_parent_carrier = _resolve_published_parent_span_context(parent_node_execution_id)
|
||||
else:
|
||||
workflow_parent_carrier = _resolve_workflow_parent_carrier(parent_node_execution_id, parent_workflow_run_id)
|
||||
|
||||
if workflow_parent_carrier is None:
|
||||
root_trace_id = _resolve_workflow_root_trace_id(trace_info)
|
||||
workflow_root_span_name: str | None = trace_info.workflow_run_id
|
||||
if not isinstance(workflow_root_span_name, str) or not workflow_root_span_name.strip():
|
||||
@@ -1176,7 +1221,7 @@ class ArizePhoenixDataTrace(BaseTraceInstance):
|
||||
if root_span_attributes:
|
||||
root_span_attributes_dict.update(root_span_attributes)
|
||||
|
||||
root_span = self.tracer.start_span(name=span_name, attributes=root_span_attributes_dict)
|
||||
root_span = self.tracer.start_span(name=span_name, attributes=root_span_attributes_dict, context=Context())
|
||||
|
||||
with use_span(root_span, end_on_exit=False):
|
||||
self.propagator.inject(carrier=carrier)
|
||||
|
||||
+208
-2
@@ -1,3 +1,5 @@
|
||||
import json
|
||||
from collections.abc import Sequence
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from types import SimpleNamespace
|
||||
from typing import Any, cast
|
||||
@@ -8,8 +10,10 @@ import pytest
|
||||
from dify_trace_arize_phoenix.arize_phoenix_trace import (
|
||||
_NODE_TYPE_TO_SPAN_KIND,
|
||||
ArizePhoenixDataTrace,
|
||||
_app_uses_phoenix_provider,
|
||||
_build_graph_parent_index,
|
||||
_get_node_span_kind,
|
||||
_parent_workflow_can_publish_span_context,
|
||||
_phoenix_parent_span_redis_key,
|
||||
_resolve_node_parent,
|
||||
_resolve_published_parent_span_context,
|
||||
@@ -25,9 +29,13 @@ from dify_trace_arize_phoenix.arize_phoenix_trace import (
|
||||
)
|
||||
from dify_trace_arize_phoenix.config import ArizeConfig, PhoenixConfig
|
||||
from openinference.semconv.trace import OpenInferenceSpanKindValues, SpanAttributes
|
||||
from opentelemetry.sdk.trace import Tracer
|
||||
from opentelemetry.context import Context
|
||||
from opentelemetry.sdk import trace as trace_sdk
|
||||
from opentelemetry.sdk.trace import ReadableSpan, Tracer
|
||||
from opentelemetry.sdk.trace.export import SimpleSpanProcessor, SpanExporter, SpanExportResult
|
||||
from opentelemetry.semconv.trace import SpanAttributes as OTELSpanAttributes
|
||||
from opentelemetry.trace import StatusCode
|
||||
from opentelemetry.trace import NonRecordingSpan, SpanContext, StatusCode, TraceFlags, TraceState, use_span
|
||||
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
|
||||
|
||||
from core.ops.entities.trace_entity import (
|
||||
DatasetRetrievalTraceInfo,
|
||||
@@ -96,6 +104,32 @@ def _get_start_span_call(start_span_mock, *, span_name: str):
|
||||
raise AssertionError(f"Could not find start_span call with name={span_name!r}")
|
||||
|
||||
|
||||
class _FakeQuery:
|
||||
def __init__(self, result):
|
||||
self._result = result
|
||||
|
||||
def filter(self, *args, **kwargs):
|
||||
return self
|
||||
|
||||
def where(self, *args, **kwargs):
|
||||
return self
|
||||
|
||||
def first(self):
|
||||
return self._result
|
||||
|
||||
|
||||
class _CollectingSpanExporter(SpanExporter):
|
||||
def __init__(self):
|
||||
self.spans: list[ReadableSpan] = []
|
||||
|
||||
def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
|
||||
self.spans.extend(spans)
|
||||
return SpanExportResult.SUCCESS
|
||||
|
||||
def shutdown(self) -> None:
|
||||
return None
|
||||
|
||||
|
||||
def _make_node_execution(**kwargs):
|
||||
defaults = {
|
||||
"node_type": "tool",
|
||||
@@ -233,6 +267,45 @@ def test_wrap_span_metadata():
|
||||
assert res == {"a": 1, "b": 2, "created_from": "Dify"}
|
||||
|
||||
|
||||
def test_app_uses_phoenix_provider_only_for_enabled_arize_or_phoenix():
|
||||
assert _app_uses_phoenix_provider({"enabled": True, "tracing_provider": "phoenix"}) is True
|
||||
assert _app_uses_phoenix_provider({"enabled": True, "tracing_provider": "arize"}) is True
|
||||
assert _app_uses_phoenix_provider({"enabled": False, "tracing_provider": "phoenix"}) is False
|
||||
assert _app_uses_phoenix_provider({"enabled": True, "tracing_provider": "langfuse"}) is False
|
||||
assert _app_uses_phoenix_provider(None) is False
|
||||
|
||||
|
||||
def test_parent_workflow_can_publish_span_context_keeps_unknown_parent_retryable(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
"dify_trace_arize_phoenix.arize_phoenix_trace.db.session.query",
|
||||
lambda model: _FakeQuery(None),
|
||||
)
|
||||
|
||||
assert _parent_workflow_can_publish_span_context("missing-run") is True
|
||||
|
||||
|
||||
def test_parent_workflow_can_publish_span_context_checks_parent_app_tracing(monkeypatch):
|
||||
parent_run = SimpleNamespace(app_id="parent-app")
|
||||
parent_app = SimpleNamespace(tracing=json.dumps({"enabled": True, "tracing_provider": "phoenix"}))
|
||||
|
||||
def fake_query(model):
|
||||
if getattr(model, "__tablename__", None) == "workflow_runs":
|
||||
return _FakeQuery(parent_run)
|
||||
if getattr(model, "__tablename__", None) == "apps":
|
||||
return _FakeQuery(parent_app)
|
||||
raise AssertionError(f"Unexpected model query: {model}")
|
||||
|
||||
monkeypatch.setattr("dify_trace_arize_phoenix.arize_phoenix_trace.db.session.query", fake_query)
|
||||
|
||||
assert _parent_workflow_can_publish_span_context("parent-run") is True
|
||||
|
||||
parent_app.tracing = json.dumps({"enabled": False, "tracing_provider": "phoenix"})
|
||||
assert _parent_workflow_can_publish_span_context("parent-run") is False
|
||||
|
||||
parent_app.tracing = json.dumps({"enabled": True, "tracing_provider": "langfuse"})
|
||||
assert _parent_workflow_can_publish_span_context("parent-run") is False
|
||||
|
||||
|
||||
class TestGetNodeSpanKind:
|
||||
def test_all_node_types_are_mapped_correctly(self):
|
||||
special_mappings = {
|
||||
@@ -839,6 +912,10 @@ def test_workflow_trace_raises_pending_parent_error_when_parent_node_context_is_
|
||||
|
||||
with (
|
||||
patch.object(trace_instance, "get_service_account_with_tenant", return_value=MagicMock()),
|
||||
patch(
|
||||
"dify_trace_arize_phoenix.arize_phoenix_trace._parent_workflow_can_publish_span_context",
|
||||
return_value=True,
|
||||
),
|
||||
patch.object(trace_instance, "ensure_root_span") as mock_ensure_root_span,
|
||||
pytest.raises(PendingTraceParentContextError) as exc_info,
|
||||
):
|
||||
@@ -851,6 +928,102 @@ def test_workflow_trace_raises_pending_parent_error_when_parent_node_context_is_
|
||||
mock_ensure_root_span.assert_not_called()
|
||||
|
||||
|
||||
@patch("dify_trace_arize_phoenix.arize_phoenix_trace.db")
|
||||
@patch("dify_trace_arize_phoenix.arize_phoenix_trace.DifyCoreRepositoryFactory")
|
||||
@patch("dify_trace_arize_phoenix.arize_phoenix_trace.sessionmaker")
|
||||
def test_workflow_trace_falls_back_when_parent_app_tracing_cannot_publish_parent_context(
|
||||
mock_sessionmaker,
|
||||
mock_repo_factory,
|
||||
mock_db,
|
||||
trace_instance,
|
||||
):
|
||||
mock_db.engine = MagicMock()
|
||||
info = _make_workflow_info(
|
||||
message_id="message-1",
|
||||
workflow_run_id="workflow-run-1",
|
||||
metadata={
|
||||
"app_id": "app1",
|
||||
"parent_trace_context": {
|
||||
"parent_workflow_run_id": "outer-workflow-run-1",
|
||||
"parent_node_execution_id": "outer-node-execution-1",
|
||||
},
|
||||
},
|
||||
)
|
||||
repo = MagicMock()
|
||||
repo.get_by_workflow_execution.return_value = []
|
||||
mock_repo_factory.create_workflow_node_execution_repository.return_value = repo
|
||||
trace_instance._mock_redis_client.get.return_value = None
|
||||
|
||||
parent_carrier = {}
|
||||
parent_context = object()
|
||||
|
||||
with (
|
||||
patch.object(trace_instance, "get_service_account_with_tenant", return_value=MagicMock()),
|
||||
patch(
|
||||
"dify_trace_arize_phoenix.arize_phoenix_trace._parent_workflow_can_publish_span_context",
|
||||
return_value=False,
|
||||
),
|
||||
patch.object(trace_instance, "ensure_root_span", return_value=parent_carrier) as mock_ensure_root_span,
|
||||
patch.object(trace_instance.propagator, "extract", return_value=parent_context) as mock_extract,
|
||||
):
|
||||
trace_instance.workflow_trace(info)
|
||||
|
||||
mock_ensure_root_span.assert_called_once_with(
|
||||
"outer-workflow-run-1",
|
||||
root_span_name="workflow-run-1",
|
||||
root_span_attributes={
|
||||
SpanAttributes.INPUT_VALUE: safe_json_dumps(info.workflow_run_inputs),
|
||||
SpanAttributes.INPUT_MIME_TYPE: "application/json",
|
||||
SpanAttributes.OUTPUT_VALUE: safe_json_dumps(info.workflow_run_outputs),
|
||||
SpanAttributes.OUTPUT_MIME_TYPE: "application/json",
|
||||
},
|
||||
)
|
||||
mock_extract.assert_called_once_with(carrier=parent_carrier)
|
||||
workflow_span_call = _get_start_span_call(trace_instance.tracer.start_span, span_name="workflow_workflow-run-1")
|
||||
assert workflow_span_call.kwargs["context"] is parent_context
|
||||
|
||||
|
||||
@patch("dify_trace_arize_phoenix.arize_phoenix_trace.db")
|
||||
@patch("dify_trace_arize_phoenix.arize_phoenix_trace.DifyCoreRepositoryFactory")
|
||||
@patch("dify_trace_arize_phoenix.arize_phoenix_trace.sessionmaker")
|
||||
def test_workflow_trace_still_retries_when_parent_app_can_publish_parent_context(
|
||||
mock_sessionmaker,
|
||||
mock_repo_factory,
|
||||
mock_db,
|
||||
trace_instance,
|
||||
):
|
||||
mock_db.engine = MagicMock()
|
||||
info = _make_workflow_info(
|
||||
message_id="message-1",
|
||||
workflow_run_id="workflow-run-1",
|
||||
metadata={
|
||||
"app_id": "app1",
|
||||
"parent_trace_context": {
|
||||
"parent_workflow_run_id": "outer-workflow-run-1",
|
||||
"parent_node_execution_id": "outer-node-execution-1",
|
||||
},
|
||||
},
|
||||
)
|
||||
repo = MagicMock()
|
||||
repo.get_by_workflow_execution.return_value = []
|
||||
mock_repo_factory.create_workflow_node_execution_repository.return_value = repo
|
||||
trace_instance._mock_redis_client.get.return_value = None
|
||||
|
||||
with (
|
||||
patch.object(trace_instance, "get_service_account_with_tenant", return_value=MagicMock()),
|
||||
patch(
|
||||
"dify_trace_arize_phoenix.arize_phoenix_trace._parent_workflow_can_publish_span_context",
|
||||
return_value=True,
|
||||
),
|
||||
patch.object(trace_instance, "ensure_root_span") as mock_ensure_root_span,
|
||||
pytest.raises(PendingTraceParentContextError) as exc_info,
|
||||
):
|
||||
trace_instance.workflow_trace(info)
|
||||
|
||||
assert exc_info.value.parent_node_execution_id == "outer-node-execution-1"
|
||||
mock_ensure_root_span.assert_not_called()
|
||||
|
||||
|
||||
@patch("dify_trace_arize_phoenix.arize_phoenix_trace.db")
|
||||
@patch("dify_trace_arize_phoenix.arize_phoenix_trace.DifyCoreRepositoryFactory")
|
||||
@patch("dify_trace_arize_phoenix.arize_phoenix_trace.sessionmaker")
|
||||
@@ -1544,6 +1717,38 @@ def test_ensure_root_span_basic(trace_instance):
|
||||
assert "tid" in trace_instance.dify_trace_ids
|
||||
|
||||
|
||||
def test_ensure_root_span_ignores_unsampled_ambient_otel_parent():
|
||||
exporter = _CollectingSpanExporter()
|
||||
provider = trace_sdk.TracerProvider()
|
||||
provider.add_span_processor(SimpleSpanProcessor(exporter))
|
||||
trace_instance = ArizePhoenixDataTrace.__new__(ArizePhoenixDataTrace)
|
||||
trace_instance.tracer = cast(Tracer, provider.get_tracer("test-phoenix-root-span"))
|
||||
trace_instance.propagator = TraceContextTextMapPropagator()
|
||||
trace_instance.project = "p"
|
||||
trace_instance.dify_trace_ids = set()
|
||||
trace_instance.root_span_carriers = {}
|
||||
trace_instance.carrier = {}
|
||||
|
||||
ambient_span_context = SpanContext(
|
||||
trace_id=0x11111111111111111111111111111111,
|
||||
span_id=0x2222222222222222,
|
||||
is_remote=True,
|
||||
trace_flags=TraceFlags(0),
|
||||
trace_state=TraceState(),
|
||||
)
|
||||
|
||||
with use_span(NonRecordingSpan(ambient_span_context), end_on_exit=False):
|
||||
carrier = trace_instance.ensure_root_span("tid")
|
||||
|
||||
assert len(exporter.spans) == 1
|
||||
root_span = exporter.spans[0]
|
||||
root_span_context = root_span.get_span_context()
|
||||
assert root_span_context is not None
|
||||
assert root_span.parent is None
|
||||
assert root_span_context.trace_id != ambient_span_context.trace_id
|
||||
assert carrier["traceparent"].split("-")[1] == f"{root_span_context.trace_id:032x}"
|
||||
|
||||
|
||||
def test_ensure_root_span_uses_custom_name_and_attributes(trace_instance):
|
||||
root_attributes = {
|
||||
SpanAttributes.INPUT_VALUE: '{"input":"value"}',
|
||||
@@ -1561,6 +1766,7 @@ def test_ensure_root_span_uses_custom_name_and_attributes(trace_instance):
|
||||
SpanAttributes.INPUT_VALUE: '{"input":"value"}',
|
||||
SpanAttributes.OUTPUT_VALUE: '{"output":"value"}',
|
||||
},
|
||||
context=Context(),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -496,6 +496,51 @@ def db_session_with_containers(flask_app_with_containers: Flask) -> Generator[Se
|
||||
logger.debug("Database session closed")
|
||||
|
||||
|
||||
def _truncate_container_database(app: Flask) -> None:
|
||||
"""
|
||||
Reset application tables after a container integration test.
|
||||
|
||||
Tests in this package share one PostgreSQL container for performance, while
|
||||
application code may commit through db.session, Session(db.engine), or
|
||||
session_factory-created sessions. Truncating after each test gives the suite
|
||||
a central DB isolation contract that does not depend on which session a test used.
|
||||
This only covers SQLAlchemy application tables in db.metadata for now;
|
||||
Redis, object storage, and custom ad hoc metadata still need their own cleanup.
|
||||
"""
|
||||
with app.app_context():
|
||||
db.session.remove()
|
||||
|
||||
tables = db.metadata.sorted_tables
|
||||
if not tables:
|
||||
return
|
||||
|
||||
preparer = db.engine.dialect.identifier_preparer
|
||||
table_names = ", ".join(preparer.format_table(table) for table in tables)
|
||||
|
||||
with db.engine.begin() as conn:
|
||||
conn.execute(text("SET LOCAL lock_timeout = '5s'"))
|
||||
conn.execute(text(f"TRUNCATE TABLE {table_names} RESTART IDENTITY CASCADE"))
|
||||
|
||||
db.session.remove()
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def isolate_container_database(request: pytest.FixtureRequest) -> Generator[None, None, None]:
|
||||
"""
|
||||
Clean DB state after tests that use the containerized Flask app.
|
||||
|
||||
This fixture intentionally does not depend on flask_app_with_containers so
|
||||
non-DB tests under this package do not start the full app/container stack.
|
||||
"""
|
||||
yield
|
||||
|
||||
if "flask_app_with_containers" not in request.fixturenames:
|
||||
return
|
||||
|
||||
app = request.getfixturevalue("flask_app_with_containers")
|
||||
_truncate_container_database(app)
|
||||
|
||||
|
||||
@pytest.fixture(scope="package", autouse=True)
|
||||
def mock_ssrf_proxy_requests():
|
||||
"""
|
||||
|
||||
@@ -5,6 +5,7 @@ from __future__ import annotations
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from flask.testing import FlaskClient
|
||||
from sqlalchemy import delete
|
||||
from sqlalchemy.orm import Session
|
||||
@@ -126,7 +127,7 @@ class TestAppApiKeyResource:
|
||||
|
||||
def test_delete_forbidden_for_non_admin(
|
||||
self,
|
||||
flask_app_with_containers,
|
||||
flask_app_with_containers: Flask,
|
||||
) -> None:
|
||||
"""A non-admin member cannot delete API keys via the controller permission check."""
|
||||
from werkzeug.exceptions import Forbidden
|
||||
|
||||
+1
-1
@@ -575,7 +575,7 @@ class TestTriggerSubscriptionVerifyApi:
|
||||
assert method(api, "github", "s1") == {"ok": True}
|
||||
|
||||
@pytest.mark.parametrize("raised_exception", [ValueError("bad"), Exception("boom")])
|
||||
def test_verify_errors(self, app, raised_exception):
|
||||
def test_verify_errors(self, app: Flask, raised_exception):
|
||||
api = TriggerSubscriptionVerifyApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
|
||||
@@ -277,7 +277,7 @@ class TestDecodeJwtToken:
|
||||
mock_extract: MagicMock,
|
||||
mock_passport_cls: MagicMock,
|
||||
mock_features: MagicMock,
|
||||
app,
|
||||
app: Flask,
|
||||
) -> None:
|
||||
non_existent_id = str(uuid4())
|
||||
mock_extract.return_value = "jwt-token"
|
||||
|
||||
+13
-12
@@ -5,6 +5,7 @@ from unittest.mock import Mock, patch
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from models.source import DataSourceApiKeyAuthBinding
|
||||
@@ -45,7 +46,7 @@ class TestApiKeyAuthService:
|
||||
return binding
|
||||
|
||||
def test_get_provider_auth_list_success(
|
||||
self, flask_app_with_containers, db_session_with_containers: Session, tenant_id, category, provider
|
||||
self, flask_app_with_containers: Flask, db_session_with_containers: Session, tenant_id, category, provider
|
||||
):
|
||||
self._create_binding(db_session_with_containers, tenant_id=tenant_id, category=category, provider=provider)
|
||||
db_session_with_containers.expire_all()
|
||||
@@ -58,7 +59,7 @@ class TestApiKeyAuthService:
|
||||
assert tenant_results[0].provider == provider
|
||||
|
||||
def test_get_provider_auth_list_empty(
|
||||
self, flask_app_with_containers, db_session_with_containers: Session, tenant_id
|
||||
self, flask_app_with_containers: Flask, db_session_with_containers: Session, tenant_id
|
||||
):
|
||||
result = ApiKeyAuthService.get_provider_auth_list(tenant_id)
|
||||
|
||||
@@ -66,7 +67,7 @@ class TestApiKeyAuthService:
|
||||
assert tenant_results == []
|
||||
|
||||
def test_get_provider_auth_list_filters_disabled(
|
||||
self, flask_app_with_containers, db_session_with_containers: Session, tenant_id, category, provider
|
||||
self, flask_app_with_containers: Flask, db_session_with_containers: Session, tenant_id, category, provider
|
||||
):
|
||||
self._create_binding(
|
||||
db_session_with_containers, tenant_id=tenant_id, category=category, provider=provider, disabled=True
|
||||
@@ -84,7 +85,7 @@ class TestApiKeyAuthService:
|
||||
self,
|
||||
mock_encrypter,
|
||||
mock_factory,
|
||||
flask_app_with_containers,
|
||||
flask_app_with_containers: Flask,
|
||||
db_session_with_containers: Session,
|
||||
tenant_id,
|
||||
mock_args,
|
||||
@@ -106,7 +107,7 @@ class TestApiKeyAuthService:
|
||||
|
||||
@patch("services.auth.api_key_auth_service.ApiKeyAuthFactory")
|
||||
def test_create_provider_auth_validation_failed(
|
||||
self, mock_factory, flask_app_with_containers, db_session_with_containers: Session, tenant_id, mock_args
|
||||
self, mock_factory, flask_app_with_containers: Flask, db_session_with_containers: Session, tenant_id, mock_args
|
||||
):
|
||||
mock_auth_instance = Mock()
|
||||
mock_auth_instance.validate_credentials.return_value = False
|
||||
@@ -124,7 +125,7 @@ class TestApiKeyAuthService:
|
||||
self,
|
||||
mock_encrypter,
|
||||
mock_factory,
|
||||
flask_app_with_containers,
|
||||
flask_app_with_containers: Flask,
|
||||
db_session_with_containers: Session,
|
||||
tenant_id,
|
||||
mock_args,
|
||||
@@ -144,7 +145,7 @@ class TestApiKeyAuthService:
|
||||
|
||||
def test_get_auth_credentials_success(
|
||||
self,
|
||||
flask_app_with_containers,
|
||||
flask_app_with_containers: Flask,
|
||||
db_session_with_containers: Session,
|
||||
tenant_id,
|
||||
category,
|
||||
@@ -165,14 +166,14 @@ class TestApiKeyAuthService:
|
||||
assert result == mock_credentials
|
||||
|
||||
def test_get_auth_credentials_not_found(
|
||||
self, flask_app_with_containers, db_session_with_containers: Session, tenant_id, category, provider
|
||||
self, flask_app_with_containers: Flask, db_session_with_containers: Session, tenant_id, category, provider
|
||||
):
|
||||
result = ApiKeyAuthService.get_auth_credentials(tenant_id, category, provider)
|
||||
|
||||
assert result is None
|
||||
|
||||
def test_get_auth_credentials_json_parsing(
|
||||
self, flask_app_with_containers, db_session_with_containers: Session, tenant_id, category, provider
|
||||
self, flask_app_with_containers: Flask, db_session_with_containers: Session, tenant_id, category, provider
|
||||
):
|
||||
special_credentials = {"auth_type": "api_key", "config": {"api_key": "key_with_中文_and_special_chars_!@#$%"}}
|
||||
self._create_binding(
|
||||
@@ -190,7 +191,7 @@ class TestApiKeyAuthService:
|
||||
assert result["config"]["api_key"] == "key_with_中文_and_special_chars_!@#$%"
|
||||
|
||||
def test_delete_provider_auth_success(
|
||||
self, flask_app_with_containers, db_session_with_containers: Session, tenant_id, category, provider
|
||||
self, flask_app_with_containers: Flask, db_session_with_containers: Session, tenant_id, category, provider
|
||||
):
|
||||
binding = self._create_binding(
|
||||
db_session_with_containers, tenant_id=tenant_id, category=category, provider=provider
|
||||
@@ -205,7 +206,7 @@ class TestApiKeyAuthService:
|
||||
assert remaining is None
|
||||
|
||||
def test_delete_provider_auth_not_found(
|
||||
self, flask_app_with_containers, db_session_with_containers: Session, tenant_id
|
||||
self, flask_app_with_containers: Flask, db_session_with_containers: Session, tenant_id
|
||||
):
|
||||
# Should not raise when binding not found
|
||||
ApiKeyAuthService.delete_provider_auth(tenant_id, str(uuid4()))
|
||||
@@ -275,7 +276,7 @@ class TestApiKeyAuthService:
|
||||
@patch("services.auth.api_key_auth_service.ApiKeyAuthFactory")
|
||||
@patch("services.auth.api_key_auth_service.encrypter")
|
||||
def test_create_provider_auth_database_error_handling(
|
||||
self, mock_encrypter, mock_factory, flask_app_with_containers, tenant_id, mock_args
|
||||
self, mock_encrypter, mock_factory, flask_app_with_containers: Flask, tenant_id, mock_args
|
||||
):
|
||||
mock_auth_instance = Mock()
|
||||
mock_auth_instance.validate_credentials.return_value = True
|
||||
|
||||
+12
-11
@@ -10,6 +10,7 @@ from uuid import uuid4
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from models.source import DataSourceApiKeyAuthBinding
|
||||
@@ -45,8 +46,8 @@ class TestAuthIntegration:
|
||||
self,
|
||||
mock_encrypt,
|
||||
mock_http,
|
||||
flask_app_with_containers,
|
||||
db_session_with_containers,
|
||||
flask_app_with_containers: Flask,
|
||||
db_session_with_containers: Session,
|
||||
tenant_id_1,
|
||||
category,
|
||||
firecrawl_credentials,
|
||||
@@ -86,8 +87,8 @@ class TestAuthIntegration:
|
||||
mock_jina_http,
|
||||
mock_fc_http,
|
||||
mock_encrypt,
|
||||
flask_app_with_containers,
|
||||
db_session_with_containers,
|
||||
flask_app_with_containers: Flask,
|
||||
db_session_with_containers: Session,
|
||||
tenant_id_1,
|
||||
tenant_id_2,
|
||||
category,
|
||||
@@ -115,7 +116,7 @@ class TestAuthIntegration:
|
||||
assert result2[0].tenant_id == tenant_id_2
|
||||
|
||||
def test_cross_tenant_access_prevention(
|
||||
self, flask_app_with_containers, db_session_with_containers: Session, tenant_id_2, category
|
||||
self, flask_app_with_containers: Flask, db_session_with_containers: Session, tenant_id_2, category
|
||||
):
|
||||
result = ApiKeyAuthService.get_auth_credentials(tenant_id_2, category, AuthType.FIRECRAWL)
|
||||
|
||||
@@ -139,8 +140,8 @@ class TestAuthIntegration:
|
||||
self,
|
||||
mock_encrypt,
|
||||
mock_http,
|
||||
flask_app_with_containers,
|
||||
db_session_with_containers,
|
||||
flask_app_with_containers: Flask,
|
||||
db_session_with_containers: Session,
|
||||
tenant_id_1,
|
||||
category,
|
||||
firecrawl_credentials,
|
||||
@@ -201,8 +202,8 @@ class TestAuthIntegration:
|
||||
def test_network_failure_recovery(
|
||||
self,
|
||||
mock_http,
|
||||
flask_app_with_containers,
|
||||
db_session_with_containers,
|
||||
flask_app_with_containers: Flask,
|
||||
db_session_with_containers: Session,
|
||||
tenant_id_1,
|
||||
category,
|
||||
firecrawl_credentials,
|
||||
@@ -239,8 +240,8 @@ class TestAuthIntegration:
|
||||
self,
|
||||
mock_http,
|
||||
mock_encrypt,
|
||||
flask_app_with_containers,
|
||||
db_session_with_containers,
|
||||
flask_app_with_containers: Flask,
|
||||
db_session_with_containers: Session,
|
||||
tenant_id_1,
|
||||
category,
|
||||
firecrawl_credentials,
|
||||
|
||||
+3
-3
@@ -14,7 +14,7 @@ from sqlalchemy.orm import Session
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
from core.rag.index_processor.constant.index_type import IndexTechniqueType
|
||||
from models import Account, Tenant, TenantAccountJoin, TenantAccountRole
|
||||
from models import Account, AccountStatus, Tenant, TenantAccountJoin, TenantAccountRole, TenantStatus
|
||||
from models.dataset import AppDatasetJoin, Dataset, DatasetPermissionEnum
|
||||
from models.enums import DataSourceType
|
||||
from models.model import App
|
||||
@@ -38,13 +38,13 @@ class DatasetUpdateDeleteTestDataFactory:
|
||||
email=f"{uuid4()}@example.com",
|
||||
name=f"user-{uuid4()}",
|
||||
interface_language="en-US",
|
||||
status="active",
|
||||
status=AccountStatus.ACTIVE,
|
||||
)
|
||||
db_session_with_containers.add(account)
|
||||
db_session_with_containers.commit()
|
||||
|
||||
if tenant is None:
|
||||
tenant = Tenant(name=f"tenant-{uuid4()}", status="normal")
|
||||
tenant = Tenant(name=f"tenant-{uuid4()}", status=TenantStatus.NORMAL)
|
||||
db_session_with_containers.add(tenant)
|
||||
db_session_with_containers.commit()
|
||||
|
||||
|
||||
+5
-4
@@ -10,6 +10,7 @@ from unittest.mock import patch
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from redis import RedisError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -123,7 +124,7 @@ class TestSyncAccountDeletion:
|
||||
mock_queue_task.assert_not_called()
|
||||
|
||||
def test_sync_account_deletion_multiple_workspaces(
|
||||
self, flask_app_with_containers, db_session_with_containers: Session, mock_queue_task
|
||||
self, flask_app_with_containers: Flask, db_session_with_containers: Session, mock_queue_task
|
||||
):
|
||||
account_id = str(uuid4())
|
||||
tenant_ids = [str(uuid4()) for _ in range(3)]
|
||||
@@ -145,7 +146,7 @@ class TestSyncAccountDeletion:
|
||||
assert queued_workspace_ids == set(tenant_ids)
|
||||
|
||||
def test_sync_account_deletion_no_workspaces(
|
||||
self, flask_app_with_containers, db_session_with_containers: Session, mock_queue_task
|
||||
self, flask_app_with_containers: Flask, db_session_with_containers: Session, mock_queue_task
|
||||
):
|
||||
with patch("services.enterprise.account_deletion_sync.dify_config") as mock_config:
|
||||
mock_config.ENTERPRISE_ENABLED = True
|
||||
@@ -156,7 +157,7 @@ class TestSyncAccountDeletion:
|
||||
mock_queue_task.assert_not_called()
|
||||
|
||||
def test_sync_account_deletion_partial_failure(
|
||||
self, flask_app_with_containers, db_session_with_containers: Session, mock_queue_task
|
||||
self, flask_app_with_containers: Flask, db_session_with_containers: Session, mock_queue_task
|
||||
):
|
||||
account_id = str(uuid4())
|
||||
tenant_ids = [str(uuid4()) for _ in range(3)]
|
||||
@@ -181,7 +182,7 @@ class TestSyncAccountDeletion:
|
||||
assert mock_queue_task.call_count == 3
|
||||
|
||||
def test_sync_account_deletion_all_failures(
|
||||
self, flask_app_with_containers, db_session_with_containers: Session, mock_queue_task
|
||||
self, flask_app_with_containers: Flask, db_session_with_containers: Session, mock_queue_task
|
||||
):
|
||||
account_id = str(uuid4())
|
||||
tenant_id = str(uuid4())
|
||||
|
||||
+5
-4
@@ -11,6 +11,7 @@ from unittest.mock import MagicMock, patch
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
|
||||
from core.plugin.entities.plugin_daemon import CredentialType
|
||||
from models.tools import BuiltinToolProvider
|
||||
@@ -49,8 +50,8 @@ class TestGetDynamicSelectOptionsTool:
|
||||
mock_tool_mgr,
|
||||
mock_encrypter_fn,
|
||||
mock_client_cls,
|
||||
flask_app_with_containers,
|
||||
db_session_with_containers,
|
||||
flask_app_with_containers: Flask,
|
||||
db_session_with_containers: Session,
|
||||
):
|
||||
tenant_id = str(uuid4())
|
||||
provider_ctrl = MagicMock()
|
||||
@@ -91,8 +92,8 @@ class TestGetDynamicSelectOptionsTool:
|
||||
self,
|
||||
mock_tool_mgr,
|
||||
mock_encrypter_fn,
|
||||
flask_app_with_containers,
|
||||
db_session_with_containers,
|
||||
flask_app_with_containers: Flask,
|
||||
db_session_with_containers: Session,
|
||||
):
|
||||
provider_ctrl = MagicMock()
|
||||
provider_ctrl.need_credentials = True
|
||||
|
||||
@@ -11,10 +11,13 @@ from unittest.mock import MagicMock, patch
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.plugin.entities.plugin import PluginInstallationSource
|
||||
from core.plugin.entities.plugin_daemon import PluginVerification
|
||||
from models import ProviderType
|
||||
from models.provider import Provider, ProviderCredential, TenantPreferredModelProvider
|
||||
from services.errors.plugin import PluginInstallationForbiddenError
|
||||
from services.feature_service import PluginInstallationScope
|
||||
@@ -346,7 +349,7 @@ class TestUninstall:
|
||||
|
||||
@patch("services.plugin.plugin_service.PluginInstaller")
|
||||
def test_cleans_credentials_when_plugin_found(
|
||||
self, mock_installer_cls, flask_app_with_containers, db_session_with_containers
|
||||
self, mock_installer_cls, flask_app_with_containers: Flask, db_session_with_containers: Session
|
||||
):
|
||||
tenant_id = str(uuid4())
|
||||
plugin_id = "org/myplugin"
|
||||
@@ -374,7 +377,7 @@ class TestUninstall:
|
||||
pref = TenantPreferredModelProvider(
|
||||
tenant_id=tenant_id,
|
||||
provider_name=provider_name,
|
||||
preferred_provider_type="custom",
|
||||
preferred_provider_type=ProviderType.CUSTOM,
|
||||
)
|
||||
db_session_with_containers.add(pref)
|
||||
db_session_with_containers.commit()
|
||||
|
||||
+18
-11
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
from unittest.mock import patch
|
||||
from uuid import uuid4
|
||||
|
||||
from flask import Flask
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from models.model import App, RecommendedApp, Site
|
||||
@@ -10,7 +11,7 @@ from services.recommend_app.database.database_retrieval import DatabaseRecommend
|
||||
from services.recommend_app.recommend_app_type import RecommendAppType
|
||||
|
||||
|
||||
def _create_app(db_session, *, tenant_id: str, is_public: bool = True) -> App:
|
||||
def _create_app(db_session: Session, *, tenant_id: str, is_public: bool = True) -> App:
|
||||
app = App(
|
||||
tenant_id=tenant_id,
|
||||
name=f"app-{uuid4()}",
|
||||
@@ -25,7 +26,7 @@ def _create_app(db_session, *, tenant_id: str, is_public: bool = True) -> App:
|
||||
return app
|
||||
|
||||
|
||||
def _create_site(db_session, *, app_id: str) -> Site:
|
||||
def _create_site(db_session: Session, *, app_id: str) -> Site:
|
||||
site = Site(
|
||||
app_id=app_id,
|
||||
title=f"site-{uuid4()}",
|
||||
@@ -95,7 +96,9 @@ class TestDatabaseRecommendAppRetrieval:
|
||||
|
||||
|
||||
class TestFetchRecommendedAppsFromDb:
|
||||
def test_returns_apps_and_sorted_categories(self, flask_app_with_containers, db_session_with_containers: Session):
|
||||
def test_returns_apps_and_sorted_categories(
|
||||
self, flask_app_with_containers: Flask, db_session_with_containers: Session
|
||||
):
|
||||
tenant_id = str(uuid4())
|
||||
app1 = _create_app(db_session_with_containers, tenant_id=tenant_id)
|
||||
_create_site(db_session_with_containers, app_id=app1.id)
|
||||
@@ -116,7 +119,7 @@ class TestFetchRecommendedAppsFromDb:
|
||||
assert "writing" in result["categories"]
|
||||
|
||||
def test_returns_multiple_categories_for_one_app(
|
||||
self, flask_app_with_containers, db_session_with_containers: Session
|
||||
self, flask_app_with_containers: Flask, db_session_with_containers: Session
|
||||
):
|
||||
tenant_id = str(uuid4())
|
||||
created_app = _create_app(db_session_with_containers, tenant_id=tenant_id)
|
||||
@@ -139,7 +142,7 @@ class TestFetchRecommendedAppsFromDb:
|
||||
|
||||
def test_ignores_legacy_category_when_categories_are_empty(
|
||||
self,
|
||||
flask_app_with_containers,
|
||||
flask_app_with_containers: Flask,
|
||||
db_session_with_containers: Session,
|
||||
):
|
||||
legacy_category = f"legacy-empty-{uuid4()}"
|
||||
@@ -163,7 +166,7 @@ class TestFetchRecommendedAppsFromDb:
|
||||
assert legacy_category not in result["categories"]
|
||||
|
||||
def test_falls_back_to_default_language_when_empty(
|
||||
self, flask_app_with_containers, db_session_with_containers: Session
|
||||
self, flask_app_with_containers: Flask, db_session_with_containers: Session
|
||||
):
|
||||
tenant_id = str(uuid4())
|
||||
app1 = _create_app(db_session_with_containers, tenant_id=tenant_id)
|
||||
@@ -177,7 +180,7 @@ class TestFetchRecommendedAppsFromDb:
|
||||
app_ids = {r["app_id"] for r in result["recommended_apps"]}
|
||||
assert app1.id in app_ids
|
||||
|
||||
def test_skips_non_public_apps(self, flask_app_with_containers, db_session_with_containers: Session):
|
||||
def test_skips_non_public_apps(self, flask_app_with_containers: Flask, db_session_with_containers: Session):
|
||||
tenant_id = str(uuid4())
|
||||
app1 = _create_app(db_session_with_containers, tenant_id=tenant_id, is_public=False)
|
||||
_create_site(db_session_with_containers, app_id=app1.id)
|
||||
@@ -190,7 +193,7 @@ class TestFetchRecommendedAppsFromDb:
|
||||
app_ids = {r["app_id"] for r in result["recommended_apps"]}
|
||||
assert app1.id not in app_ids
|
||||
|
||||
def test_skips_apps_without_site(self, flask_app_with_containers, db_session_with_containers: Session):
|
||||
def test_skips_apps_without_site(self, flask_app_with_containers: Flask, db_session_with_containers: Session):
|
||||
tenant_id = str(uuid4())
|
||||
app1 = _create_app(db_session_with_containers, tenant_id=tenant_id)
|
||||
_create_recommended_app(db_session_with_containers, app_id=app1.id)
|
||||
@@ -204,12 +207,14 @@ class TestFetchRecommendedAppsFromDb:
|
||||
|
||||
|
||||
class TestFetchRecommendedAppDetailFromDb:
|
||||
def test_returns_none_when_not_listed(self, flask_app_with_containers, db_session_with_containers: Session):
|
||||
def test_returns_none_when_not_listed(self, flask_app_with_containers: Flask, db_session_with_containers: Session):
|
||||
result = DatabaseRecommendAppRetrieval.fetch_recommended_app_detail_from_db(str(uuid4()))
|
||||
|
||||
assert result is None
|
||||
|
||||
def test_returns_none_when_app_not_public(self, flask_app_with_containers, db_session_with_containers: Session):
|
||||
def test_returns_none_when_app_not_public(
|
||||
self, flask_app_with_containers: Flask, db_session_with_containers: Session
|
||||
):
|
||||
tenant_id = str(uuid4())
|
||||
app1 = _create_app(db_session_with_containers, tenant_id=tenant_id, is_public=False)
|
||||
_create_recommended_app(db_session_with_containers, app_id=app1.id)
|
||||
@@ -221,7 +226,9 @@ class TestFetchRecommendedAppDetailFromDb:
|
||||
assert result is None
|
||||
|
||||
@patch("services.recommend_app.database.database_retrieval.AppDslService")
|
||||
def test_returns_detail_on_success(self, mock_dsl, flask_app_with_containers, db_session_with_containers: Session):
|
||||
def test_returns_detail_on_success(
|
||||
self, mock_dsl, flask_app_with_containers: Flask, db_session_with_containers: Session
|
||||
):
|
||||
tenant_id = str(uuid4())
|
||||
app1 = _create_app(db_session_with_containers, tenant_id=tenant_id)
|
||||
_create_site(db_session_with_containers, app_id=app1.id)
|
||||
|
||||
@@ -6,7 +6,7 @@ from faker import Faker
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.plugin.impl.exc import PluginDaemonClientSideError
|
||||
from models import Account, CreatorUserRole
|
||||
from models import Account, AppMode, CreatorUserRole
|
||||
from models.enums import ConversationFromSource, MessageFileBelongsTo
|
||||
from models.model import AppModelConfig, Conversation, EndUser, Message, MessageAgentThought
|
||||
from services.account_service import AccountService, TenantService
|
||||
@@ -134,7 +134,7 @@ class TestAgentService:
|
||||
app = app_service.create_app(tenant.id, app_args, account)
|
||||
|
||||
# Update the app model config to set agent_mode for agent-chat mode
|
||||
if app.mode == "agent-chat" and app.app_model_config:
|
||||
if app.mode == AppMode.AGENT_CHAT and app.app_model_config:
|
||||
app.app_model_config.agent_mode = json.dumps({"enabled": True, "strategy": "react", "tools": []})
|
||||
|
||||
db_session_with_containers.commit()
|
||||
@@ -272,7 +272,7 @@ class TestAgentService:
|
||||
tool_input=json.dumps({"dataset_tool": {"query": "test_query"}}),
|
||||
observation=json.dumps({"dataset_tool": {"results": "test_results"}}),
|
||||
tokens=30,
|
||||
created_by_role="account",
|
||||
created_by_role=CreatorUserRole.ACCOUNT,
|
||||
created_by=message.from_account_id,
|
||||
)
|
||||
db_session_with_containers.add(thought2)
|
||||
|
||||
@@ -5,6 +5,7 @@ from unittest.mock import MagicMock, patch
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from werkzeug.exceptions import Unauthorized
|
||||
|
||||
import services.api_token_service as api_token_service_module
|
||||
@@ -14,7 +15,7 @@ from services.api_token_service import ApiTokenCache, CachedApiToken
|
||||
|
||||
class TestQueryTokenFromDb:
|
||||
def test_should_return_api_token_and_cache_when_token_exists(
|
||||
self, flask_app_with_containers, db_session_with_containers
|
||||
self, flask_app_with_containers: Flask, db_session_with_containers
|
||||
):
|
||||
tenant_id = str(uuid4())
|
||||
app_id = str(uuid4())
|
||||
@@ -41,7 +42,7 @@ class TestQueryTokenFromDb:
|
||||
mock_record_usage.assert_called_once_with(token_value, "app")
|
||||
|
||||
def test_should_cache_null_and_raise_unauthorized_when_token_not_found(
|
||||
self, flask_app_with_containers, db_session_with_containers
|
||||
self, flask_app_with_containers: Flask, db_session_with_containers
|
||||
):
|
||||
with (
|
||||
patch.object(api_token_service_module.ApiTokenCache, "set") as mock_cache_set,
|
||||
|
||||
+5
-4
@@ -15,6 +15,7 @@ from uuid import uuid4
|
||||
from zipfile import ZipFile
|
||||
|
||||
import pytest
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
import services.file_service as file_service_module
|
||||
from extensions.storage.storage_type import StorageType
|
||||
@@ -23,7 +24,7 @@ from models.model import UploadFile
|
||||
from services.file_service import FileService
|
||||
|
||||
|
||||
def _create_upload_file(db_session, *, tenant_id: str, key: str, name: str) -> UploadFile:
|
||||
def _create_upload_file(db_session: Session, *, tenant_id: str, key: str, name: str) -> UploadFile:
|
||||
upload_file = UploadFile(
|
||||
tenant_id=tenant_id,
|
||||
storage_type=StorageType.OPENDAL,
|
||||
@@ -66,12 +67,12 @@ def test_build_upload_files_zip_tempfile_sanitizes_and_dedupes_names(monkeypatch
|
||||
assert zf.read("b (2).txt") == b"three"
|
||||
|
||||
|
||||
def test_get_upload_files_by_ids_returns_empty_when_no_ids(db_session_with_containers) -> None:
|
||||
def test_get_upload_files_by_ids_returns_empty_when_no_ids(db_session_with_containers: Session) -> None:
|
||||
"""Ensure empty input returns an empty mapping without hitting the database."""
|
||||
assert FileService.get_upload_files_by_ids(str(uuid4()), []) == {}
|
||||
|
||||
|
||||
def test_get_upload_files_by_ids_returns_id_keyed_mapping(db_session_with_containers) -> None:
|
||||
def test_get_upload_files_by_ids_returns_id_keyed_mapping(db_session_with_containers: Session) -> None:
|
||||
"""Ensure batch lookup returns a dict keyed by stringified UploadFile ids."""
|
||||
tenant_id = str(uuid4())
|
||||
file1 = _create_upload_file(db_session_with_containers, tenant_id=tenant_id, key="k1", name="file1.txt")
|
||||
@@ -84,7 +85,7 @@ def test_get_upload_files_by_ids_returns_id_keyed_mapping(db_session_with_contai
|
||||
assert result[file2.id].id == file2.id
|
||||
|
||||
|
||||
def test_get_upload_files_by_ids_filters_by_tenant(db_session_with_containers) -> None:
|
||||
def test_get_upload_files_by_ids_filters_by_tenant(db_session_with_containers: Session) -> None:
|
||||
"""Ensure files from other tenants are not returned."""
|
||||
tenant_a = str(uuid4())
|
||||
tenant_b = str(uuid4())
|
||||
|
||||
+6
-3
@@ -5,6 +5,7 @@ from unittest.mock import MagicMock, patch
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from sqlalchemy.engine import Engine
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -89,7 +90,7 @@ class TestDeliveryTestRegistry:
|
||||
with pytest.raises(DeliveryTestUnsupportedError, match="Delivery method does not support test send."):
|
||||
registry.dispatch(context=context, method=method)
|
||||
|
||||
def test_default(self, flask_app_with_containers, db_session_with_containers: Session):
|
||||
def test_default(self, flask_app_with_containers: Flask, db_session_with_containers: Session):
|
||||
registry = DeliveryTestRegistry.default()
|
||||
assert len(registry._handlers) == 1
|
||||
assert isinstance(registry._handlers[0], EmailDeliveryTestHandler)
|
||||
@@ -261,7 +262,7 @@ class TestEmailDeliveryTestHandler:
|
||||
)
|
||||
assert handler._resolve_recipients(tenant_id="t1", method=method) == ["ext@example.com"]
|
||||
|
||||
def test_resolve_recipients_member(self, flask_app_with_containers, db_session_with_containers: Session):
|
||||
def test_resolve_recipients_member(self, flask_app_with_containers: Flask, db_session_with_containers: Session):
|
||||
tenant_id = str(uuid4())
|
||||
account = Account(name="Test User", email="member@example.com")
|
||||
db_session_with_containers.add(account)
|
||||
@@ -283,7 +284,9 @@ class TestEmailDeliveryTestHandler:
|
||||
)
|
||||
assert handler._resolve_recipients(tenant_id=tenant_id, method=method) == ["member@example.com"]
|
||||
|
||||
def test_resolve_recipients_whole_workspace(self, flask_app_with_containers, db_session_with_containers: Session):
|
||||
def test_resolve_recipients_whole_workspace(
|
||||
self, flask_app_with_containers: Flask, db_session_with_containers: Session
|
||||
):
|
||||
tenant_id = str(uuid4())
|
||||
account1 = Account(name="User 1", email=f"u1-{uuid4()}@example.com")
|
||||
account2 = Account(name="User 2", email=f"u2-{uuid4()}@example.com")
|
||||
|
||||
+26
-6
@@ -4,6 +4,7 @@ from unittest.mock import Mock, patch
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -17,7 +18,7 @@ from services.entities.knowledge_entities.knowledge_entities import (
|
||||
from services.metadata_service import MetadataService
|
||||
|
||||
|
||||
def _create_dataset(db_session, *, tenant_id: str, built_in_field_enabled: bool = False) -> Dataset:
|
||||
def _create_dataset(db_session: Session, *, tenant_id: str, built_in_field_enabled: bool = False) -> Dataset:
|
||||
dataset = Dataset(
|
||||
tenant_id=tenant_id,
|
||||
name=f"dataset-{uuid4()}",
|
||||
@@ -31,7 +32,9 @@ def _create_dataset(db_session, *, tenant_id: str, built_in_field_enabled: bool
|
||||
return dataset
|
||||
|
||||
|
||||
def _create_document(db_session, *, dataset_id: str, tenant_id: str, doc_metadata: dict | None = None) -> Document:
|
||||
def _create_document(
|
||||
db_session: Session, *, dataset_id: str, tenant_id: str, doc_metadata: dict | None = None
|
||||
) -> Document:
|
||||
document = Document(
|
||||
tenant_id=tenant_id,
|
||||
dataset_id=dataset_id,
|
||||
@@ -66,7 +69,11 @@ class TestMetadataPartialUpdate:
|
||||
yield account
|
||||
|
||||
def test_partial_update_merges_metadata(
|
||||
self, flask_app_with_containers, db_session_with_containers: Session, tenant_id, mock_current_account
|
||||
self,
|
||||
flask_app_with_containers: Flask,
|
||||
db_session_with_containers: Session,
|
||||
tenant_id: str,
|
||||
mock_current_account,
|
||||
):
|
||||
dataset = _create_dataset(db_session_with_containers, tenant_id=tenant_id)
|
||||
document = _create_document(
|
||||
@@ -93,7 +100,11 @@ class TestMetadataPartialUpdate:
|
||||
assert updated_doc.doc_metadata["new_key"] == "new_value"
|
||||
|
||||
def test_full_update_replaces_metadata(
|
||||
self, flask_app_with_containers, db_session_with_containers: Session, tenant_id, mock_current_account
|
||||
self,
|
||||
flask_app_with_containers: Flask,
|
||||
db_session_with_containers: Session,
|
||||
tenant_id: str,
|
||||
mock_current_account,
|
||||
):
|
||||
dataset = _create_dataset(db_session_with_containers, tenant_id=tenant_id)
|
||||
document = _create_document(
|
||||
@@ -120,7 +131,12 @@ class TestMetadataPartialUpdate:
|
||||
assert "existing_key" not in updated_doc.doc_metadata
|
||||
|
||||
def test_partial_update_skips_existing_binding(
|
||||
self, flask_app_with_containers, db_session_with_containers: Session, tenant_id, user_id, mock_current_account
|
||||
self,
|
||||
flask_app_with_containers: Flask,
|
||||
db_session_with_containers: Session,
|
||||
tenant_id,
|
||||
user_id,
|
||||
mock_current_account,
|
||||
):
|
||||
dataset = _create_dataset(db_session_with_containers, tenant_id=tenant_id)
|
||||
document = _create_document(
|
||||
@@ -160,7 +176,11 @@ class TestMetadataPartialUpdate:
|
||||
assert len(bindings) == 1
|
||||
|
||||
def test_rollback_called_on_commit_failure(
|
||||
self, flask_app_with_containers, db_session_with_containers: Session, tenant_id, mock_current_account
|
||||
self,
|
||||
flask_app_with_containers: Flask,
|
||||
db_session_with_containers: Session,
|
||||
tenant_id: str,
|
||||
mock_current_account,
|
||||
):
|
||||
dataset = _create_dataset(db_session_with_containers, tenant_id=tenant_id)
|
||||
document = _create_document(
|
||||
|
||||
@@ -6,6 +6,7 @@ from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
|
||||
from controllers.console.app import app_import as app_import_module
|
||||
from services.app_dsl_service import ImportStatus
|
||||
@@ -48,7 +49,9 @@ class TestAppImportApi:
|
||||
def api(self):
|
||||
return app_import_module.AppImportApi()
|
||||
|
||||
def test_import_post_returns_failed_status_and_rolls_back(self, api, app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def test_import_post_returns_failed_status_and_rolls_back(
|
||||
self, api, app: Flask, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
method = _unwrap(api.post)
|
||||
|
||||
_install_features(monkeypatch, enabled=False)
|
||||
@@ -68,7 +71,9 @@ class TestAppImportApi:
|
||||
assert status == 400
|
||||
assert response["status"] == ImportStatus.FAILED
|
||||
|
||||
def test_import_post_returns_pending_status_and_commits(self, api, app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def test_import_post_returns_pending_status_and_commits(
|
||||
self, api, app: Flask, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
method = _unwrap(api.post)
|
||||
|
||||
_install_features(monkeypatch, enabled=False)
|
||||
@@ -88,7 +93,9 @@ class TestAppImportApi:
|
||||
assert status == 202
|
||||
assert response["status"] == ImportStatus.PENDING
|
||||
|
||||
def test_import_post_updates_webapp_auth_when_enabled(self, api, app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def test_import_post_updates_webapp_auth_when_enabled(
|
||||
self, api, app: Flask, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
method = _unwrap(api.post)
|
||||
|
||||
_install_features(monkeypatch, enabled=True)
|
||||
@@ -118,7 +125,7 @@ class TestAppImportConfirmApi:
|
||||
return app_import_module.AppImportConfirmApi()
|
||||
|
||||
def test_import_confirm_returns_failed_status_and_rolls_back(
|
||||
self, api, app, monkeypatch: pytest.MonkeyPatch
|
||||
self, api, app: Flask, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
method = _unwrap(api.post)
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from datetime import UTC, datetime
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from pydantic import ValidationError
|
||||
|
||||
from controllers.console.app import conversation_variables as conversation_variables_module
|
||||
@@ -20,7 +21,7 @@ def _unwrap(func):
|
||||
return func
|
||||
|
||||
|
||||
def test_get_conversation_variables_returns_paginated_response(app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def test_get_conversation_variables_returns_paginated_response(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
api = conversation_variables_module.ConversationVariablesApi()
|
||||
method = _unwrap(api.get)
|
||||
|
||||
@@ -63,7 +64,9 @@ def test_get_conversation_variables_returns_paginated_response(app, monkeypatch:
|
||||
assert response["data"][0]["updated_at"] == int(updated_at.timestamp())
|
||||
|
||||
|
||||
def test_get_conversation_variables_normalizes_value_type_and_value(app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def test_get_conversation_variables_normalizes_value_type_and_value(
|
||||
app: Flask, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
api = conversation_variables_module.ConversationVariablesApi()
|
||||
method = _unwrap(api.get)
|
||||
|
||||
|
||||
@@ -20,8 +20,11 @@ from models.workflow import WorkflowRun
|
||||
|
||||
|
||||
def _make_account() -> Account:
|
||||
account = Account(name="tester", email="tester@example.com")
|
||||
account.status = AccountStatus.ACTIVE
|
||||
account = Account(
|
||||
name="tester",
|
||||
email="tester@example.com",
|
||||
status=AccountStatus.ACTIVE,
|
||||
)
|
||||
account.role = TenantAccountRole.OWNER
|
||||
account.id = "account-123" # type: ignore[assignment]
|
||||
account._current_tenant = SimpleNamespace(id="tenant-123") # type: ignore[attr-defined]
|
||||
|
||||
+12
-12
@@ -1,7 +1,7 @@
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from flask import Response
|
||||
from flask import Flask, Response
|
||||
|
||||
from controllers.console import console_ns
|
||||
from controllers.console.app.error import DraftWorkflowNotExist
|
||||
@@ -46,7 +46,7 @@ def restx_config(app):
|
||||
|
||||
|
||||
class TestRagPipelineVariableCollectionApi:
|
||||
def test_get_variables_success(self, app, fake_db, editor_user, restx_config):
|
||||
def test_get_variables_success(self, app: Flask, fake_db, editor_user, restx_config):
|
||||
api = RagPipelineVariableCollectionApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -80,7 +80,7 @@ class TestRagPipelineVariableCollectionApi:
|
||||
|
||||
assert result["items"] == []
|
||||
|
||||
def test_get_variables_workflow_not_exist(self, app, fake_db, editor_user):
|
||||
def test_get_variables_workflow_not_exist(self, app: Flask, fake_db, editor_user):
|
||||
api = RagPipelineVariableCollectionApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -101,7 +101,7 @@ class TestRagPipelineVariableCollectionApi:
|
||||
with pytest.raises(DraftWorkflowNotExist):
|
||||
method(api, pipeline)
|
||||
|
||||
def test_delete_variables_success(self, app, fake_db, editor_user):
|
||||
def test_delete_variables_success(self, app: Flask, fake_db, editor_user):
|
||||
api = RagPipelineVariableCollectionApi()
|
||||
method = unwrap(api.delete)
|
||||
|
||||
@@ -120,7 +120,7 @@ class TestRagPipelineVariableCollectionApi:
|
||||
|
||||
|
||||
class TestRagPipelineNodeVariableCollectionApi:
|
||||
def test_get_node_variables_success(self, app, fake_db, editor_user, restx_config):
|
||||
def test_get_node_variables_success(self, app: Flask, fake_db, editor_user, restx_config):
|
||||
api = RagPipelineNodeVariableCollectionApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -146,7 +146,7 @@ class TestRagPipelineNodeVariableCollectionApi:
|
||||
|
||||
assert result["items"] == []
|
||||
|
||||
def test_get_node_variables_invalid_node(self, app, editor_user):
|
||||
def test_get_node_variables_invalid_node(self, app: Flask, editor_user):
|
||||
api = RagPipelineNodeVariableCollectionApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -159,7 +159,7 @@ class TestRagPipelineNodeVariableCollectionApi:
|
||||
|
||||
|
||||
class TestRagPipelineVariableApi:
|
||||
def test_get_variable_not_found(self, app, fake_db, editor_user):
|
||||
def test_get_variable_not_found(self, app: Flask, fake_db, editor_user):
|
||||
api = RagPipelineVariableApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -178,7 +178,7 @@ class TestRagPipelineVariableApi:
|
||||
with pytest.raises(NotFoundError):
|
||||
method(api, MagicMock(), "v1")
|
||||
|
||||
def test_patch_variable_invalid_file_payload(self, app, fake_db, editor_user):
|
||||
def test_patch_variable_invalid_file_payload(self, app: Flask, fake_db, editor_user):
|
||||
api = RagPipelineVariableApi()
|
||||
method = unwrap(api.patch)
|
||||
|
||||
@@ -203,7 +203,7 @@ class TestRagPipelineVariableApi:
|
||||
with pytest.raises(InvalidArgumentError):
|
||||
method(api, pipeline, "v1")
|
||||
|
||||
def test_delete_variable_success(self, app, fake_db, editor_user):
|
||||
def test_delete_variable_success(self, app: Flask, fake_db, editor_user):
|
||||
api = RagPipelineVariableApi()
|
||||
method = unwrap(api.delete)
|
||||
|
||||
@@ -228,7 +228,7 @@ class TestRagPipelineVariableApi:
|
||||
|
||||
|
||||
class TestRagPipelineVariableResetApi:
|
||||
def test_reset_variable_success(self, app, fake_db, editor_user):
|
||||
def test_reset_variable_success(self, app: Flask, fake_db, editor_user):
|
||||
api = RagPipelineVariableResetApi()
|
||||
method = unwrap(api.put)
|
||||
|
||||
@@ -266,7 +266,7 @@ class TestRagPipelineVariableResetApi:
|
||||
|
||||
|
||||
class TestSystemAndEnvironmentVariablesApi:
|
||||
def test_system_variables_success(self, app, fake_db, editor_user, restx_config):
|
||||
def test_system_variables_success(self, app: Flask, fake_db, editor_user, restx_config):
|
||||
api = RagPipelineSystemVariableCollectionApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -292,7 +292,7 @@ class TestSystemAndEnvironmentVariablesApi:
|
||||
|
||||
assert result["items"] == []
|
||||
|
||||
def test_environment_variables_success(self, app, editor_user):
|
||||
def test_environment_variables_success(self, app: Flask, editor_user):
|
||||
api = RagPipelineEnvironmentVariableCollectionApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ def patch_permission():
|
||||
|
||||
|
||||
class TestGetProcessRuleApi:
|
||||
def test_get_default_success(self, app, patch_tenant):
|
||||
def test_get_default_success(self, app: Flask, patch_tenant):
|
||||
api = GetProcessRuleApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -104,7 +104,7 @@ class TestGetProcessRuleApi:
|
||||
|
||||
assert "rules" in response
|
||||
|
||||
def test_get_with_document_dataset_not_found(self, app, patch_tenant):
|
||||
def test_get_with_document_dataset_not_found(self, app: Flask, patch_tenant):
|
||||
api = GetProcessRuleApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -126,7 +126,7 @@ class TestGetProcessRuleApi:
|
||||
|
||||
|
||||
class TestDatasetDocumentListApi:
|
||||
def test_get_with_fetch_true_counts_segments(self, app, patch_tenant, patch_dataset, patch_permission):
|
||||
def test_get_with_fetch_true_counts_segments(self, app: Flask, patch_tenant, patch_dataset, patch_permission):
|
||||
api = DatasetDocumentListApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -158,7 +158,9 @@ class TestDatasetDocumentListApi:
|
||||
|
||||
assert resp["data"]
|
||||
|
||||
def test_get_with_search_status_and_created_at_sort(self, app, patch_tenant, patch_dataset, patch_permission):
|
||||
def test_get_with_search_status_and_created_at_sort(
|
||||
self, app: Flask, patch_tenant, patch_dataset, patch_permission
|
||||
):
|
||||
api = DatasetDocumentListApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -187,7 +189,7 @@ class TestDatasetDocumentListApi:
|
||||
|
||||
assert resp["total"] == 1
|
||||
|
||||
def test_get_success(self, app, patch_tenant, patch_dataset, patch_permission):
|
||||
def test_get_success(self, app: Flask, patch_tenant, patch_dataset, patch_permission):
|
||||
api = DatasetDocumentListApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -212,7 +214,7 @@ class TestDatasetDocumentListApi:
|
||||
|
||||
assert response["total"] == 1
|
||||
|
||||
def test_post_success(self, app, patch_tenant, patch_dataset, patch_permission):
|
||||
def test_post_success(self, app: Flask, patch_tenant, patch_dataset, patch_permission):
|
||||
api = DatasetDocumentListApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -261,7 +263,7 @@ class TestDatasetDocumentListApi:
|
||||
with pytest.raises(Forbidden):
|
||||
method(api, "ds-1")
|
||||
|
||||
def test_get_with_fetch_true_and_invalid_fetch(self, app, patch_tenant, patch_dataset, patch_permission):
|
||||
def test_get_with_fetch_true_and_invalid_fetch(self, app: Flask, patch_tenant, patch_dataset, patch_permission):
|
||||
api = DatasetDocumentListApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -286,7 +288,7 @@ class TestDatasetDocumentListApi:
|
||||
|
||||
assert response["total"] == 1
|
||||
|
||||
def test_get_sort_hit_count(self, app, patch_tenant, patch_dataset, patch_permission):
|
||||
def test_get_sort_hit_count(self, app: Flask, patch_tenant, patch_dataset, patch_permission):
|
||||
api = DatasetDocumentListApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -309,7 +311,7 @@ class TestDatasetDocumentListApi:
|
||||
|
||||
|
||||
class TestDocumentApi:
|
||||
def test_get_success(self, app, patch_tenant):
|
||||
def test_get_success(self, app: Flask, patch_tenant):
|
||||
api = DocumentApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -327,7 +329,7 @@ class TestDocumentApi:
|
||||
|
||||
assert status == 200
|
||||
|
||||
def test_get_invalid_metadata(self, app, patch_tenant):
|
||||
def test_get_invalid_metadata(self, app: Flask, patch_tenant):
|
||||
api = DocumentApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -335,7 +337,7 @@ class TestDocumentApi:
|
||||
with pytest.raises(InvalidMetadataError):
|
||||
method(api, "ds-1", "doc-1")
|
||||
|
||||
def test_delete_success(self, app, patch_tenant, patch_dataset):
|
||||
def test_delete_success(self, app: Flask, patch_tenant, patch_dataset):
|
||||
api = DocumentApi()
|
||||
method = unwrap(api.delete)
|
||||
|
||||
@@ -355,7 +357,7 @@ class TestDocumentApi:
|
||||
|
||||
assert status == 204
|
||||
|
||||
def test_delete_indexing_error(self, app, patch_tenant, patch_dataset):
|
||||
def test_delete_indexing_error(self, app: Flask, patch_tenant, patch_dataset):
|
||||
api = DocumentApi()
|
||||
method = unwrap(api.delete)
|
||||
|
||||
@@ -376,7 +378,7 @@ class TestDocumentApi:
|
||||
|
||||
|
||||
class TestDocumentDownloadApi:
|
||||
def test_download_success(self, app, patch_tenant):
|
||||
def test_download_success(self, app: Flask, patch_tenant):
|
||||
api = DocumentDownloadApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -413,7 +415,7 @@ class TestDocumentProcessingApi:
|
||||
with pytest.raises(Forbidden):
|
||||
method(api, "ds-1", "doc-1", "pause")
|
||||
|
||||
def test_resume_from_error_state(self, app, patch_tenant):
|
||||
def test_resume_from_error_state(self, app: Flask, patch_tenant):
|
||||
api = DocumentProcessingApi()
|
||||
method = unwrap(api.patch)
|
||||
|
||||
@@ -431,7 +433,7 @@ class TestDocumentProcessingApi:
|
||||
|
||||
assert status == 200
|
||||
|
||||
def test_resume_success(self, app, patch_tenant):
|
||||
def test_resume_success(self, app: Flask, patch_tenant):
|
||||
api = DocumentProcessingApi()
|
||||
method = unwrap(api.patch)
|
||||
|
||||
@@ -449,7 +451,7 @@ class TestDocumentProcessingApi:
|
||||
|
||||
assert status == 200
|
||||
|
||||
def test_pause_success(self, app, patch_tenant):
|
||||
def test_pause_success(self, app: Flask, patch_tenant):
|
||||
api = DocumentProcessingApi()
|
||||
method = unwrap(api.patch)
|
||||
|
||||
@@ -467,7 +469,7 @@ class TestDocumentProcessingApi:
|
||||
|
||||
assert status == 200
|
||||
|
||||
def test_pause_invalid(self, app, patch_tenant):
|
||||
def test_pause_invalid(self, app: Flask, patch_tenant):
|
||||
api = DocumentProcessingApi()
|
||||
method = unwrap(api.patch)
|
||||
|
||||
@@ -479,7 +481,7 @@ class TestDocumentProcessingApi:
|
||||
|
||||
|
||||
class TestDocumentMetadataApi:
|
||||
def test_put_metadata_schema_filtering(self, app, patch_tenant):
|
||||
def test_put_metadata_schema_filtering(self, app: Flask, patch_tenant):
|
||||
api = DocumentMetadataApi()
|
||||
method = unwrap(api.put)
|
||||
|
||||
@@ -508,7 +510,7 @@ class TestDocumentMetadataApi:
|
||||
|
||||
assert doc.doc_metadata == {"amount": 10}
|
||||
|
||||
def test_put_success(self, app, patch_tenant):
|
||||
def test_put_success(self, app: Flask, patch_tenant):
|
||||
api = DocumentMetadataApi()
|
||||
method = unwrap(api.put)
|
||||
|
||||
@@ -532,7 +534,7 @@ class TestDocumentMetadataApi:
|
||||
|
||||
assert status == 200
|
||||
|
||||
def test_put_invalid_payload(self, app, patch_tenant):
|
||||
def test_put_invalid_payload(self, app: Flask, patch_tenant):
|
||||
api = DocumentMetadataApi()
|
||||
method = unwrap(api.put)
|
||||
|
||||
@@ -540,7 +542,7 @@ class TestDocumentMetadataApi:
|
||||
with pytest.raises(ValueError):
|
||||
method(api, "ds-1", "doc-1")
|
||||
|
||||
def test_put_invalid_doc_type(self, app, patch_tenant):
|
||||
def test_put_invalid_doc_type(self, app: Flask, patch_tenant):
|
||||
api = DocumentMetadataApi()
|
||||
method = unwrap(api.put)
|
||||
|
||||
@@ -559,7 +561,7 @@ class TestDocumentMetadataApi:
|
||||
|
||||
|
||||
class TestDocumentStatusApi:
|
||||
def test_patch_success(self, app, patch_tenant, patch_dataset):
|
||||
def test_patch_success(self, app: Flask, patch_tenant, patch_dataset):
|
||||
api = DocumentStatusApi()
|
||||
method = unwrap(api.patch)
|
||||
|
||||
@@ -582,7 +584,7 @@ class TestDocumentStatusApi:
|
||||
|
||||
assert status == 200
|
||||
|
||||
def test_patch_invalid_action(self, app, patch_tenant, patch_dataset):
|
||||
def test_patch_invalid_action(self, app: Flask, patch_tenant, patch_dataset):
|
||||
api = DocumentStatusApi()
|
||||
method = unwrap(api.patch)
|
||||
|
||||
@@ -606,7 +608,7 @@ class TestDocumentStatusApi:
|
||||
|
||||
|
||||
class TestDocumentRetryApi:
|
||||
def test_retry_archived_document_skipped(self, app, patch_tenant, patch_dataset):
|
||||
def test_retry_archived_document_skipped(self, app: Flask, patch_tenant, patch_dataset):
|
||||
api = DocumentRetryApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -634,7 +636,7 @@ class TestDocumentRetryApi:
|
||||
assert status == 204
|
||||
retry_mock.assert_called_once_with("ds-1", [])
|
||||
|
||||
def test_retry_success(self, app, patch_tenant, patch_dataset):
|
||||
def test_retry_success(self, app: Flask, patch_tenant, patch_dataset):
|
||||
api = DocumentRetryApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -663,7 +665,7 @@ class TestDocumentRetryApi:
|
||||
assert status == 204
|
||||
retry_mock.assert_called_once_with("ds-1", [document])
|
||||
|
||||
def test_retry_skips_completed_document(self, app, patch_tenant, patch_dataset):
|
||||
def test_retry_skips_completed_document(self, app: Flask, patch_tenant, patch_dataset):
|
||||
api = DocumentRetryApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -690,7 +692,7 @@ class TestDocumentRetryApi:
|
||||
|
||||
|
||||
class TestDocumentPipelineExecutionLogApi:
|
||||
def test_get_log_success(self, app, patch_tenant, patch_dataset):
|
||||
def test_get_log_success(self, app: Flask, patch_tenant, patch_dataset):
|
||||
api = DocumentPipelineExecutionLogApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -718,7 +720,7 @@ class TestDocumentPipelineExecutionLogApi:
|
||||
|
||||
|
||||
class TestDocumentGenerateSummaryApi:
|
||||
def test_generate_summary_missing_documents(self, app, patch_tenant, patch_permission):
|
||||
def test_generate_summary_missing_documents(self, app: Flask, patch_tenant, patch_permission):
|
||||
api = DocumentGenerateSummaryApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -744,7 +746,7 @@ class TestDocumentGenerateSummaryApi:
|
||||
with pytest.raises(NotFound):
|
||||
method(api, "ds-1")
|
||||
|
||||
def test_generate_not_enabled(self, app, patch_tenant, patch_permission):
|
||||
def test_generate_not_enabled(self, app: Flask, patch_tenant, patch_permission):
|
||||
api = DocumentGenerateSummaryApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -763,7 +765,7 @@ class TestDocumentGenerateSummaryApi:
|
||||
with pytest.raises(ValueError):
|
||||
method(api, "ds-1")
|
||||
|
||||
def test_generate_summary_success_with_qa_skip(self, app, patch_tenant, patch_permission):
|
||||
def test_generate_summary_success_with_qa_skip(self, app: Flask, patch_tenant, patch_permission):
|
||||
api = DocumentGenerateSummaryApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -799,7 +801,7 @@ class TestDocumentGenerateSummaryApi:
|
||||
|
||||
|
||||
class TestDocumentSummaryStatusApi:
|
||||
def test_get_success(self, app, patch_tenant, patch_permission):
|
||||
def test_get_success(self, app: Flask, patch_tenant, patch_permission):
|
||||
api = DocumentSummaryStatusApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -820,7 +822,7 @@ class TestDocumentSummaryStatusApi:
|
||||
|
||||
|
||||
class TestDocumentIndexingEstimateApi:
|
||||
def test_indexing_estimate_file_not_found(self, app, patch_tenant):
|
||||
def test_indexing_estimate_file_not_found(self, app: Flask, patch_tenant):
|
||||
api = DocumentIndexingEstimateApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -844,7 +846,7 @@ class TestDocumentIndexingEstimateApi:
|
||||
with pytest.raises(NotFound):
|
||||
method(api, "ds-1", "doc-1")
|
||||
|
||||
def test_indexing_estimate_generic_exception(self, app, patch_tenant):
|
||||
def test_indexing_estimate_generic_exception(self, app: Flask, patch_tenant):
|
||||
api = DocumentIndexingEstimateApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -881,7 +883,7 @@ class TestDocumentIndexingEstimateApi:
|
||||
with pytest.raises(IndexingEstimateError):
|
||||
method(api, "ds-1", "doc-1")
|
||||
|
||||
def test_get_finished(self, app, patch_tenant):
|
||||
def test_get_finished(self, app: Flask, patch_tenant):
|
||||
api = DocumentIndexingEstimateApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -893,7 +895,7 @@ class TestDocumentIndexingEstimateApi:
|
||||
|
||||
|
||||
class TestDocumentBatchDownloadZipApi:
|
||||
def test_post_no_documents(self, app, patch_tenant):
|
||||
def test_post_no_documents(self, app: Flask, patch_tenant):
|
||||
api = DocumentBatchDownloadZipApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -905,7 +907,7 @@ class TestDocumentBatchDownloadZipApi:
|
||||
|
||||
|
||||
class TestDatasetDocumentListApiDelete:
|
||||
def test_delete_success(self, app, patch_tenant, patch_dataset):
|
||||
def test_delete_success(self, app: Flask, patch_tenant, patch_dataset):
|
||||
"""Test successful deletion of documents"""
|
||||
api = DatasetDocumentListApi()
|
||||
method = unwrap(api.delete)
|
||||
@@ -925,7 +927,7 @@ class TestDatasetDocumentListApiDelete:
|
||||
|
||||
assert status == 204
|
||||
|
||||
def test_delete_indexing_error(self, app, patch_tenant, patch_dataset):
|
||||
def test_delete_indexing_error(self, app: Flask, patch_tenant, patch_dataset):
|
||||
"""Test deletion with indexing error"""
|
||||
api = DatasetDocumentListApi()
|
||||
method = unwrap(api.delete)
|
||||
@@ -944,7 +946,7 @@ class TestDatasetDocumentListApiDelete:
|
||||
with pytest.raises(DocumentIndexingError):
|
||||
method(api, "ds-1")
|
||||
|
||||
def test_delete_dataset_not_found(self, app, patch_tenant):
|
||||
def test_delete_dataset_not_found(self, app: Flask, patch_tenant):
|
||||
"""Test deletion when dataset not found"""
|
||||
api = DatasetDocumentListApi()
|
||||
method = unwrap(api.delete)
|
||||
@@ -961,7 +963,7 @@ class TestDatasetDocumentListApiDelete:
|
||||
|
||||
|
||||
class TestDocumentBatchIndexingEstimateApi:
|
||||
def test_batch_indexing_estimate_website(self, app, patch_tenant):
|
||||
def test_batch_indexing_estimate_website(self, app: Flask, patch_tenant):
|
||||
api = DocumentBatchIndexingEstimateApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -990,7 +992,7 @@ class TestDocumentBatchIndexingEstimateApi:
|
||||
|
||||
assert status == 200
|
||||
|
||||
def test_batch_indexing_estimate_notion(self, app, patch_tenant):
|
||||
def test_batch_indexing_estimate_notion(self, app: Flask, patch_tenant):
|
||||
api = DocumentBatchIndexingEstimateApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -1018,7 +1020,7 @@ class TestDocumentBatchIndexingEstimateApi:
|
||||
|
||||
assert status == 200
|
||||
|
||||
def test_batch_estimate_unsupported_datasource(self, app, patch_tenant):
|
||||
def test_batch_estimate_unsupported_datasource(self, app: Flask, patch_tenant):
|
||||
api = DocumentBatchIndexingEstimateApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -1033,7 +1035,7 @@ class TestDocumentBatchIndexingEstimateApi:
|
||||
with pytest.raises(ValueError):
|
||||
method(api, "ds-1", "batch-1")
|
||||
|
||||
def test_get_batch_estimate_invalid_batch(self, app, patch_tenant):
|
||||
def test_get_batch_estimate_invalid_batch(self, app: Flask, patch_tenant):
|
||||
"""Test batch estimation with invalid batch"""
|
||||
api = DocumentBatchIndexingEstimateApi()
|
||||
method = unwrap(api.get)
|
||||
@@ -1044,7 +1046,7 @@ class TestDocumentBatchIndexingEstimateApi:
|
||||
|
||||
|
||||
class TestDocumentBatchIndexingStatusApi:
|
||||
def test_get_batch_status_invalid_batch(self, app, patch_tenant):
|
||||
def test_get_batch_status_invalid_batch(self, app: Flask, patch_tenant):
|
||||
"""Test batch status with invalid batch"""
|
||||
api = DocumentBatchIndexingStatusApi()
|
||||
method = unwrap(api.get)
|
||||
@@ -1055,7 +1057,7 @@ class TestDocumentBatchIndexingStatusApi:
|
||||
|
||||
|
||||
class TestDocumentIndexingStatusApi:
|
||||
def test_get_status_document_not_found(self, app, patch_tenant):
|
||||
def test_get_status_document_not_found(self, app: Flask, patch_tenant):
|
||||
"""Test getting status for non-existent document"""
|
||||
api = DocumentIndexingStatusApi()
|
||||
method = unwrap(api.get)
|
||||
@@ -1066,7 +1068,7 @@ class TestDocumentIndexingStatusApi:
|
||||
|
||||
|
||||
class TestDocumentApiMetadata:
|
||||
def test_get_with_only_option(self, app, patch_tenant):
|
||||
def test_get_with_only_option(self, app: Flask, patch_tenant):
|
||||
"""Test get with 'only' metadata option"""
|
||||
api = DocumentApi()
|
||||
method = unwrap(api.get)
|
||||
@@ -1085,7 +1087,7 @@ class TestDocumentApiMetadata:
|
||||
|
||||
assert status == 200
|
||||
|
||||
def test_get_with_without_option(self, app, patch_tenant):
|
||||
def test_get_with_without_option(self, app: Flask, patch_tenant):
|
||||
"""Test get with 'without' metadata option"""
|
||||
api = DocumentApi()
|
||||
method = unwrap(api.get)
|
||||
@@ -1106,7 +1108,7 @@ class TestDocumentApiMetadata:
|
||||
|
||||
|
||||
class TestDocumentGenerateSummaryApiSuccess:
|
||||
def test_generate_not_enabled_high_quality(self, app, patch_tenant, patch_permission):
|
||||
def test_generate_not_enabled_high_quality(self, app: Flask, patch_tenant, patch_permission):
|
||||
"""Test summary generation on non-high-quality dataset"""
|
||||
api = DocumentGenerateSummaryApi()
|
||||
method = unwrap(api.post)
|
||||
@@ -1128,7 +1130,7 @@ class TestDocumentGenerateSummaryApiSuccess:
|
||||
|
||||
|
||||
class TestDocumentProcessingApiResume:
|
||||
def test_resume_invalid_status(self, app, patch_tenant):
|
||||
def test_resume_invalid_status(self, app: Flask, patch_tenant):
|
||||
"""Test resume on non-paused document"""
|
||||
api = DocumentProcessingApi()
|
||||
method = unwrap(api.patch)
|
||||
@@ -1141,7 +1143,7 @@ class TestDocumentProcessingApiResume:
|
||||
|
||||
|
||||
class TestDocumentPermissionCases:
|
||||
def test_document_batch_get_permission_denied(self, app, patch_tenant):
|
||||
def test_document_batch_get_permission_denied(self, app: Flask, patch_tenant):
|
||||
api = DocumentBatchIndexingEstimateApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -1159,7 +1161,7 @@ class TestDocumentPermissionCases:
|
||||
with pytest.raises(Forbidden):
|
||||
method(api, "ds-1", "batch-1")
|
||||
|
||||
def test_document_batch_get_documents_not_found(self, app, patch_tenant):
|
||||
def test_document_batch_get_documents_not_found(self, app: Flask, patch_tenant):
|
||||
api = DocumentBatchIndexingEstimateApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -1218,7 +1220,7 @@ class TestDocumentPermissionCases:
|
||||
with pytest.raises(Forbidden):
|
||||
method(api, "ds-1", "doc-1")
|
||||
|
||||
def test_process_rule_get_by_document_success(self, app, patch_tenant):
|
||||
def test_process_rule_get_by_document_success(self, app: Flask, patch_tenant):
|
||||
api = GetProcessRuleApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -1284,7 +1286,7 @@ class TestDocumentPermissionCases:
|
||||
|
||||
|
||||
class TestDocumentListAdvancedCases:
|
||||
def test_document_list_with_multiple_sort_options(self, app, patch_tenant, patch_dataset, patch_permission):
|
||||
def test_document_list_with_multiple_sort_options(self, app: Flask, patch_tenant, patch_dataset, patch_permission):
|
||||
"""Test document list with different sort options"""
|
||||
api = DatasetDocumentListApi()
|
||||
method = unwrap(api.get)
|
||||
@@ -1310,7 +1312,7 @@ class TestDocumentListAdvancedCases:
|
||||
|
||||
assert response["total"] == 1
|
||||
|
||||
def test_document_metadata_with_schema_validation(self, app, patch_tenant):
|
||||
def test_document_metadata_with_schema_validation(self, app: Flask, patch_tenant):
|
||||
"""Test document metadata update with schema validation"""
|
||||
api = DocumentMetadataApi()
|
||||
method = unwrap(api.put)
|
||||
@@ -1342,7 +1344,7 @@ class TestDocumentListAdvancedCases:
|
||||
|
||||
|
||||
class TestDocumentIndexingEdgeCases:
|
||||
def test_document_indexing_with_extraction_setting(self, app, patch_tenant):
|
||||
def test_document_indexing_with_extraction_setting(self, app: Flask, patch_tenant):
|
||||
api = DocumentIndexingEstimateApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
|
||||
@@ -292,7 +292,7 @@ class TestBedrockRetrievalApi:
|
||||
|
||||
|
||||
class TestExternalApiTemplateListApiAdvanced:
|
||||
def test_post_duplicate_name_error(self, app, mock_auth, current_user):
|
||||
def test_post_duplicate_name_error(self, app: Flask, mock_auth, current_user):
|
||||
api = ExternalApiTemplateListApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -310,7 +310,7 @@ class TestExternalApiTemplateListApiAdvanced:
|
||||
with pytest.raises(DatasetNameDuplicateError):
|
||||
method(api)
|
||||
|
||||
def test_get_with_pagination(self, app, mock_auth, current_user):
|
||||
def test_get_with_pagination(self, app: Flask, mock_auth, current_user):
|
||||
api = ExternalApiTemplateListApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -331,7 +331,7 @@ class TestExternalApiTemplateListApiAdvanced:
|
||||
|
||||
|
||||
class TestExternalDatasetCreateApiAdvanced:
|
||||
def test_create_forbidden(self, app, mock_auth, current_user):
|
||||
def test_create_forbidden(self, app: Flask, mock_auth, current_user):
|
||||
"""Test creating external dataset without permission"""
|
||||
api = ExternalDatasetCreateApi()
|
||||
method = unwrap(api.post)
|
||||
@@ -351,7 +351,7 @@ class TestExternalDatasetCreateApiAdvanced:
|
||||
|
||||
|
||||
class TestExternalKnowledgeHitTestingApiAdvanced:
|
||||
def test_hit_testing_dataset_not_found(self, app, mock_auth, current_user):
|
||||
def test_hit_testing_dataset_not_found(self, app: Flask, mock_auth, current_user):
|
||||
"""Test hit testing on non-existent dataset"""
|
||||
api = ExternalKnowledgeHitTestingApi()
|
||||
method = unwrap(api.post)
|
||||
@@ -372,7 +372,7 @@ class TestExternalKnowledgeHitTestingApiAdvanced:
|
||||
with pytest.raises(NotFound):
|
||||
method(api, "ds-1")
|
||||
|
||||
def test_hit_testing_with_custom_retrieval_model(self, app, mock_auth, current_user):
|
||||
def test_hit_testing_with_custom_retrieval_model(self, app: Flask, mock_auth, current_user):
|
||||
api = ExternalKnowledgeHitTestingApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -402,7 +402,7 @@ class TestExternalKnowledgeHitTestingApiAdvanced:
|
||||
|
||||
|
||||
class TestBedrockRetrievalApiAdvanced:
|
||||
def test_bedrock_retrieval_with_invalid_setting(self, app, mock_auth, current_user):
|
||||
def test_bedrock_retrieval_with_invalid_setting(self, app: Flask, mock_auth, current_user):
|
||||
api = BedrockRetrievalApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ def bypass_decorators(mocker: MockerFixture):
|
||||
|
||||
|
||||
class TestDatasetMetadataCreateApi:
|
||||
def test_create_metadata_success(self, app, current_user, dataset, dataset_id):
|
||||
def test_create_metadata_success(self, app: Flask, current_user, dataset, dataset_id):
|
||||
api = DatasetMetadataCreateApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -125,7 +125,7 @@ class TestDatasetMetadataCreateApi:
|
||||
assert status == 201
|
||||
assert result["name"] == "author"
|
||||
|
||||
def test_create_metadata_dataset_not_found(self, app, current_user, dataset_id):
|
||||
def test_create_metadata_dataset_not_found(self, app: Flask, current_user, dataset_id):
|
||||
api = DatasetMetadataCreateApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -162,7 +162,7 @@ class TestDatasetMetadataCreateApi:
|
||||
|
||||
|
||||
class TestDatasetMetadataGetApi:
|
||||
def test_get_metadata_success(self, app, dataset, dataset_id):
|
||||
def test_get_metadata_success(self, app: Flask, dataset, dataset_id):
|
||||
api = DatasetMetadataCreateApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -184,7 +184,7 @@ class TestDatasetMetadataGetApi:
|
||||
assert status == 200
|
||||
assert isinstance(result, list)
|
||||
|
||||
def test_get_metadata_dataset_not_found(self, app, dataset_id):
|
||||
def test_get_metadata_dataset_not_found(self, app: Flask, dataset_id):
|
||||
api = DatasetMetadataCreateApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -201,7 +201,7 @@ class TestDatasetMetadataGetApi:
|
||||
|
||||
|
||||
class TestDatasetMetadataApi:
|
||||
def test_update_metadata_success(self, app, current_user, dataset, dataset_id, metadata_id):
|
||||
def test_update_metadata_success(self, app: Flask, current_user, dataset, dataset_id, metadata_id):
|
||||
api = DatasetMetadataApi()
|
||||
method = unwrap(api.patch)
|
||||
|
||||
@@ -239,7 +239,7 @@ class TestDatasetMetadataApi:
|
||||
assert status == 200
|
||||
assert result["name"] == "updated-name"
|
||||
|
||||
def test_delete_metadata_success(self, app, current_user, dataset, dataset_id, metadata_id):
|
||||
def test_delete_metadata_success(self, app: Flask, current_user, dataset, dataset_id, metadata_id):
|
||||
api = DatasetMetadataApi()
|
||||
method = unwrap(api.delete)
|
||||
|
||||
@@ -289,7 +289,7 @@ class TestDatasetMetadataBuiltInFieldApi:
|
||||
|
||||
|
||||
class TestDatasetMetadataBuiltInFieldActionApi:
|
||||
def test_enable_built_in_field(self, app, current_user, dataset, dataset_id):
|
||||
def test_enable_built_in_field(self, app: Flask, current_user, dataset, dataset_id):
|
||||
api = DatasetMetadataBuiltInFieldActionApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -320,7 +320,7 @@ class TestDatasetMetadataBuiltInFieldActionApi:
|
||||
|
||||
|
||||
class TestDocumentMetadataEditApi:
|
||||
def test_update_document_metadata_success(self, app, current_user, dataset, dataset_id):
|
||||
def test_update_document_metadata_success(self, app: Flask, current_user, dataset, dataset_id):
|
||||
api = DocumentMetadataEditApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ def bypass_auth_and_setup(mocker: MockerFixture):
|
||||
|
||||
|
||||
class TestWebsiteCrawlApi:
|
||||
def test_crawl_success(self, app, mocker: MockerFixture):
|
||||
def test_crawl_success(self, app: Flask, mocker: MockerFixture):
|
||||
api = WebsiteCrawlApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -86,7 +86,7 @@ class TestWebsiteCrawlApi:
|
||||
assert status == 200
|
||||
assert result["job_id"] == "job-1"
|
||||
|
||||
def test_crawl_invalid_payload(self, app, mocker: MockerFixture):
|
||||
def test_crawl_invalid_payload(self, app: Flask, mocker: MockerFixture):
|
||||
api = WebsiteCrawlApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -114,7 +114,7 @@ class TestWebsiteCrawlApi:
|
||||
with pytest.raises(WebsiteCrawlError, match="invalid payload"):
|
||||
method(api)
|
||||
|
||||
def test_crawl_service_error(self, app, mocker: MockerFixture):
|
||||
def test_crawl_service_error(self, app: Flask, mocker: MockerFixture):
|
||||
api = WebsiteCrawlApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -151,7 +151,7 @@ class TestWebsiteCrawlApi:
|
||||
|
||||
|
||||
class TestWebsiteCrawlStatusApi:
|
||||
def test_get_status_success(self, app, mocker: MockerFixture):
|
||||
def test_get_status_success(self, app: Flask, mocker: MockerFixture):
|
||||
api = WebsiteCrawlStatusApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -182,7 +182,7 @@ class TestWebsiteCrawlStatusApi:
|
||||
assert status == 200
|
||||
assert result["status"] == "completed"
|
||||
|
||||
def test_get_status_invalid_provider(self, app, mocker: MockerFixture):
|
||||
def test_get_status_invalid_provider(self, app: Flask, mocker: MockerFixture):
|
||||
api = WebsiteCrawlStatusApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -204,7 +204,7 @@ class TestWebsiteCrawlStatusApi:
|
||||
with pytest.raises(WebsiteCrawlError, match="invalid provider"):
|
||||
method(api, job_id)
|
||||
|
||||
def test_get_status_service_error(self, app, mocker: MockerFixture):
|
||||
def test_get_status_service_error(self, app: Flask, mocker: MockerFixture):
|
||||
api = WebsiteCrawlStatusApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ from io import BytesIO
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from werkzeug.exceptions import InternalServerError
|
||||
|
||||
import controllers.console.explore.audio as audio_module
|
||||
@@ -52,7 +53,7 @@ class TestChatAudioApi:
|
||||
self.api = audio_module.ChatAudioApi()
|
||||
self.method = unwrap(self.api.post)
|
||||
|
||||
def test_post_success(self, app, installed_app, audio_file):
|
||||
def test_post_success(self, app: Flask, installed_app, audio_file):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -69,7 +70,7 @@ class TestChatAudioApi:
|
||||
|
||||
assert resp == {"text": "ok"}
|
||||
|
||||
def test_app_unavailable(self, app, installed_app, audio_file):
|
||||
def test_app_unavailable(self, app: Flask, installed_app, audio_file):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -85,7 +86,7 @@ class TestChatAudioApi:
|
||||
with pytest.raises(AppUnavailableError):
|
||||
self.method(installed_app)
|
||||
|
||||
def test_no_audio_uploaded(self, app, installed_app, audio_file):
|
||||
def test_no_audio_uploaded(self, app: Flask, installed_app, audio_file):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -101,7 +102,7 @@ class TestChatAudioApi:
|
||||
with pytest.raises(NoAudioUploadedError):
|
||||
self.method(installed_app)
|
||||
|
||||
def test_audio_too_large(self, app, installed_app, audio_file):
|
||||
def test_audio_too_large(self, app: Flask, installed_app, audio_file):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -117,7 +118,7 @@ class TestChatAudioApi:
|
||||
with pytest.raises(AudioTooLargeError):
|
||||
self.method(installed_app)
|
||||
|
||||
def test_provider_quota_exceeded(self, app, installed_app, audio_file):
|
||||
def test_provider_quota_exceeded(self, app: Flask, installed_app, audio_file):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -133,7 +134,7 @@ class TestChatAudioApi:
|
||||
with pytest.raises(ProviderQuotaExceededError):
|
||||
self.method(installed_app)
|
||||
|
||||
def test_unknown_exception(self, app, installed_app, audio_file):
|
||||
def test_unknown_exception(self, app: Flask, installed_app, audio_file):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -149,7 +150,7 @@ class TestChatAudioApi:
|
||||
with pytest.raises(InternalServerError):
|
||||
self.method(installed_app)
|
||||
|
||||
def test_unsupported_audio_type(self, app, installed_app, audio_file):
|
||||
def test_unsupported_audio_type(self, app: Flask, installed_app, audio_file):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -165,7 +166,7 @@ class TestChatAudioApi:
|
||||
with pytest.raises(audio_module.UnsupportedAudioTypeError):
|
||||
self.method(installed_app)
|
||||
|
||||
def test_provider_not_support_speech_to_text(self, app, installed_app, audio_file):
|
||||
def test_provider_not_support_speech_to_text(self, app: Flask, installed_app, audio_file):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -181,7 +182,7 @@ class TestChatAudioApi:
|
||||
with pytest.raises(audio_module.ProviderNotSupportSpeechToTextError):
|
||||
self.method(installed_app)
|
||||
|
||||
def test_provider_not_initialized(self, app, installed_app, audio_file):
|
||||
def test_provider_not_initialized(self, app: Flask, installed_app, audio_file):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -197,7 +198,7 @@ class TestChatAudioApi:
|
||||
with pytest.raises(ProviderNotInitializeError):
|
||||
self.method(installed_app)
|
||||
|
||||
def test_model_currently_not_supported(self, app, installed_app, audio_file):
|
||||
def test_model_currently_not_supported(self, app: Flask, installed_app, audio_file):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -213,7 +214,7 @@ class TestChatAudioApi:
|
||||
with pytest.raises(ProviderModelCurrentlyNotSupportError):
|
||||
self.method(installed_app)
|
||||
|
||||
def test_invoke_error_asr(self, app, installed_app, audio_file):
|
||||
def test_invoke_error_asr(self, app: Flask, installed_app, audio_file):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -235,7 +236,7 @@ class TestChatTextApi:
|
||||
self.api = audio_module.ChatTextApi()
|
||||
self.method = unwrap(self.api.post)
|
||||
|
||||
def test_post_success(self, app, installed_app):
|
||||
def test_post_success(self, app: Flask, installed_app):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -251,7 +252,7 @@ class TestChatTextApi:
|
||||
|
||||
assert resp == {"audio": "ok"}
|
||||
|
||||
def test_provider_not_initialized(self, app, installed_app):
|
||||
def test_provider_not_initialized(self, app: Flask, installed_app):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -266,7 +267,7 @@ class TestChatTextApi:
|
||||
with pytest.raises(ProviderNotInitializeError):
|
||||
self.method(installed_app)
|
||||
|
||||
def test_model_not_supported(self, app, installed_app):
|
||||
def test_model_not_supported(self, app: Flask, installed_app):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -281,7 +282,7 @@ class TestChatTextApi:
|
||||
with pytest.raises(ProviderModelCurrentlyNotSupportError):
|
||||
self.method(installed_app)
|
||||
|
||||
def test_invoke_error(self, app, installed_app):
|
||||
def test_invoke_error(self, app: Flask, installed_app):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -296,7 +297,7 @@ class TestChatTextApi:
|
||||
with pytest.raises(CompletionRequestError):
|
||||
self.method(installed_app)
|
||||
|
||||
def test_unknown_exception(self, app, installed_app):
|
||||
def test_unknown_exception(self, app: Flask, installed_app):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -311,7 +312,7 @@ class TestChatTextApi:
|
||||
with pytest.raises(InternalServerError):
|
||||
self.method(installed_app)
|
||||
|
||||
def test_app_unavailable_tts(self, app, installed_app):
|
||||
def test_app_unavailable_tts(self, app: Flask, installed_app):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -326,7 +327,7 @@ class TestChatTextApi:
|
||||
with pytest.raises(AppUnavailableError):
|
||||
self.method(installed_app)
|
||||
|
||||
def test_no_audio_uploaded_tts(self, app, installed_app):
|
||||
def test_no_audio_uploaded_tts(self, app: Flask, installed_app):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -341,7 +342,7 @@ class TestChatTextApi:
|
||||
with pytest.raises(NoAudioUploadedError):
|
||||
self.method(installed_app)
|
||||
|
||||
def test_audio_too_large_tts(self, app, installed_app):
|
||||
def test_audio_too_large_tts(self, app: Flask, installed_app):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -356,7 +357,7 @@ class TestChatTextApi:
|
||||
with pytest.raises(AudioTooLargeError):
|
||||
self.method(installed_app)
|
||||
|
||||
def test_unsupported_audio_type_tts(self, app, installed_app):
|
||||
def test_unsupported_audio_type_tts(self, app: Flask, installed_app):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -371,7 +372,7 @@ class TestChatTextApi:
|
||||
with pytest.raises(audio_module.UnsupportedAudioTypeError):
|
||||
self.method(installed_app)
|
||||
|
||||
def test_provider_not_support_speech_to_text_tts(self, app, installed_app):
|
||||
def test_provider_not_support_speech_to_text_tts(self, app: Flask, installed_app):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
@@ -386,7 +387,7 @@ class TestChatTextApi:
|
||||
with pytest.raises(audio_module.ProviderNotSupportSpeechToTextError):
|
||||
self.method(installed_app)
|
||||
|
||||
def test_quota_exceeded_tts(self, app, installed_app):
|
||||
def test_quota_exceeded_tts(self, app: Flask, installed_app):
|
||||
with (
|
||||
app.test_request_context(
|
||||
"/",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from unittest.mock import MagicMock, PropertyMock, patch
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from werkzeug.exceptions import InternalServerError
|
||||
|
||||
import controllers.console.explore.completion as completion_module
|
||||
@@ -51,7 +52,7 @@ def payload_patch(payload_data):
|
||||
|
||||
|
||||
class TestCompletionApi:
|
||||
def test_post_success(self, app, completion_app, user, payload_patch):
|
||||
def test_post_success(self, app: Flask, completion_app, user, payload_patch):
|
||||
api = completion_module.CompletionApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -83,7 +84,7 @@ class TestCompletionApi:
|
||||
with pytest.raises(NotCompletionAppError):
|
||||
method(installed_app)
|
||||
|
||||
def test_conversation_completed(self, app, completion_app, user, payload_patch):
|
||||
def test_conversation_completed(self, app: Flask, completion_app, user, payload_patch):
|
||||
api = completion_module.CompletionApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -100,7 +101,7 @@ class TestCompletionApi:
|
||||
with pytest.raises(ConversationCompletedError):
|
||||
method(completion_app)
|
||||
|
||||
def test_internal_error(self, app, completion_app, user, payload_patch):
|
||||
def test_internal_error(self, app: Flask, completion_app, user, payload_patch):
|
||||
api = completion_module.CompletionApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -117,7 +118,7 @@ class TestCompletionApi:
|
||||
with pytest.raises(InternalServerError):
|
||||
method(completion_app)
|
||||
|
||||
def test_conversation_not_exists(self, app, completion_app, user, payload_patch):
|
||||
def test_conversation_not_exists(self, app: Flask, completion_app, user, payload_patch):
|
||||
api = completion_module.CompletionApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -134,7 +135,7 @@ class TestCompletionApi:
|
||||
with pytest.raises(completion_module.NotFound):
|
||||
method(completion_app)
|
||||
|
||||
def test_app_unavailable(self, app, completion_app, user, payload_patch):
|
||||
def test_app_unavailable(self, app: Flask, completion_app, user, payload_patch):
|
||||
api = completion_module.CompletionApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -151,7 +152,7 @@ class TestCompletionApi:
|
||||
with pytest.raises(completion_module.AppUnavailableError):
|
||||
method(completion_app)
|
||||
|
||||
def test_provider_not_initialized(self, app, completion_app, user, payload_patch):
|
||||
def test_provider_not_initialized(self, app: Flask, completion_app, user, payload_patch):
|
||||
api = completion_module.CompletionApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -168,7 +169,7 @@ class TestCompletionApi:
|
||||
with pytest.raises(completion_module.ProviderNotInitializeError):
|
||||
method(completion_app)
|
||||
|
||||
def test_quota_exceeded(self, app, completion_app, user, payload_patch):
|
||||
def test_quota_exceeded(self, app: Flask, completion_app, user, payload_patch):
|
||||
api = completion_module.CompletionApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -185,7 +186,7 @@ class TestCompletionApi:
|
||||
with pytest.raises(completion_module.ProviderQuotaExceededError):
|
||||
method(completion_app)
|
||||
|
||||
def test_model_not_supported(self, app, completion_app, user, payload_patch):
|
||||
def test_model_not_supported(self, app: Flask, completion_app, user, payload_patch):
|
||||
api = completion_module.CompletionApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -202,7 +203,7 @@ class TestCompletionApi:
|
||||
with pytest.raises(completion_module.ProviderModelCurrentlyNotSupportError):
|
||||
method(completion_app)
|
||||
|
||||
def test_invoke_error(self, app, completion_app, user, payload_patch):
|
||||
def test_invoke_error(self, app: Flask, completion_app, user, payload_patch):
|
||||
api = completion_module.CompletionApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -247,7 +248,7 @@ class TestCompletionStopApi:
|
||||
|
||||
|
||||
class TestChatApi:
|
||||
def test_post_success(self, app, chat_app, user, payload_patch):
|
||||
def test_post_success(self, app: Flask, chat_app, user, payload_patch):
|
||||
api = completion_module.ChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -279,7 +280,7 @@ class TestChatApi:
|
||||
with pytest.raises(NotChatAppError):
|
||||
method(installed_app)
|
||||
|
||||
def test_rate_limit_error(self, app, chat_app, user, payload_patch):
|
||||
def test_rate_limit_error(self, app: Flask, chat_app, user, payload_patch):
|
||||
api = completion_module.ChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -296,7 +297,7 @@ class TestChatApi:
|
||||
with pytest.raises(InvokeRateLimitHttpError):
|
||||
method(chat_app)
|
||||
|
||||
def test_conversation_completed_chat(self, app, chat_app, user, payload_patch):
|
||||
def test_conversation_completed_chat(self, app: Flask, chat_app, user, payload_patch):
|
||||
api = completion_module.ChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -313,7 +314,7 @@ class TestChatApi:
|
||||
with pytest.raises(ConversationCompletedError):
|
||||
method(chat_app)
|
||||
|
||||
def test_conversation_not_exists_chat(self, app, chat_app, user, payload_patch):
|
||||
def test_conversation_not_exists_chat(self, app: Flask, chat_app, user, payload_patch):
|
||||
api = completion_module.ChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -330,7 +331,7 @@ class TestChatApi:
|
||||
with pytest.raises(completion_module.NotFound):
|
||||
method(chat_app)
|
||||
|
||||
def test_app_unavailable_chat(self, app, chat_app, user, payload_patch):
|
||||
def test_app_unavailable_chat(self, app: Flask, chat_app, user, payload_patch):
|
||||
api = completion_module.ChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -347,7 +348,7 @@ class TestChatApi:
|
||||
with pytest.raises(completion_module.AppUnavailableError):
|
||||
method(chat_app)
|
||||
|
||||
def test_provider_not_initialized_chat(self, app, chat_app, user, payload_patch):
|
||||
def test_provider_not_initialized_chat(self, app: Flask, chat_app, user, payload_patch):
|
||||
api = completion_module.ChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -364,7 +365,7 @@ class TestChatApi:
|
||||
with pytest.raises(completion_module.ProviderNotInitializeError):
|
||||
method(chat_app)
|
||||
|
||||
def test_quota_exceeded_chat(self, app, chat_app, user, payload_patch):
|
||||
def test_quota_exceeded_chat(self, app: Flask, chat_app, user, payload_patch):
|
||||
api = completion_module.ChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -381,7 +382,7 @@ class TestChatApi:
|
||||
with pytest.raises(completion_module.ProviderQuotaExceededError):
|
||||
method(chat_app)
|
||||
|
||||
def test_model_not_supported_chat(self, app, chat_app, user, payload_patch):
|
||||
def test_model_not_supported_chat(self, app: Flask, chat_app, user, payload_patch):
|
||||
api = completion_module.ChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -398,7 +399,7 @@ class TestChatApi:
|
||||
with pytest.raises(completion_module.ProviderModelCurrentlyNotSupportError):
|
||||
method(chat_app)
|
||||
|
||||
def test_invoke_error_chat(self, app, chat_app, user, payload_patch):
|
||||
def test_invoke_error_chat(self, app: Flask, chat_app, user, payload_patch):
|
||||
api = completion_module.ChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -415,7 +416,7 @@ class TestChatApi:
|
||||
with pytest.raises(completion_module.CompletionRequestError):
|
||||
method(chat_app)
|
||||
|
||||
def test_internal_error_chat(self, app, chat_app, user, payload_patch):
|
||||
def test_internal_error_chat(self, app: Flask, chat_app, user, payload_patch):
|
||||
api = completion_module.ChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ from datetime import datetime
|
||||
from unittest.mock import MagicMock, PropertyMock, patch
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from werkzeug.exceptions import BadRequest, Forbidden, NotFound
|
||||
|
||||
import controllers.console.explore.installed_app as module
|
||||
@@ -51,7 +52,7 @@ def payload_patch():
|
||||
|
||||
|
||||
class TestInstalledAppsListApi:
|
||||
def test_get_installed_apps(self, app, current_user, tenant_id, installed_app):
|
||||
def test_get_installed_apps(self, app: Flask, current_user, tenant_id, installed_app):
|
||||
api = module.InstalledAppsListApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -75,7 +76,7 @@ class TestInstalledAppsListApi:
|
||||
assert result["installed_apps"][0]["editable"] is True
|
||||
assert result["installed_apps"][0]["uninstallable"] is False
|
||||
|
||||
def test_get_installed_apps_with_app_id_filter(self, app, current_user, tenant_id):
|
||||
def test_get_installed_apps_with_app_id_filter(self, app: Flask, current_user, tenant_id):
|
||||
api = module.InstalledAppsListApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -97,7 +98,7 @@ class TestInstalledAppsListApi:
|
||||
|
||||
assert result == {"installed_apps": []}
|
||||
|
||||
def test_get_installed_apps_with_webapp_auth_enabled(self, app, current_user, tenant_id, installed_app):
|
||||
def test_get_installed_apps_with_webapp_auth_enabled(self, app: Flask, current_user, tenant_id, installed_app):
|
||||
"""Test filtering when webapp_auth is enabled."""
|
||||
api = module.InstalledAppsListApi()
|
||||
method = unwrap(api.get)
|
||||
@@ -133,7 +134,7 @@ class TestInstalledAppsListApi:
|
||||
|
||||
assert len(result["installed_apps"]) == 1
|
||||
|
||||
def test_get_installed_apps_with_webapp_auth_user_denied(self, app, current_user, tenant_id, installed_app):
|
||||
def test_get_installed_apps_with_webapp_auth_user_denied(self, app: Flask, current_user, tenant_id, installed_app):
|
||||
"""Test filtering when user doesn't have access."""
|
||||
api = module.InstalledAppsListApi()
|
||||
method = unwrap(api.get)
|
||||
@@ -169,7 +170,7 @@ class TestInstalledAppsListApi:
|
||||
|
||||
assert result["installed_apps"] == []
|
||||
|
||||
def test_get_installed_apps_with_sso_verified_access(self, app, current_user, tenant_id, installed_app):
|
||||
def test_get_installed_apps_with_sso_verified_access(self, app: Flask, current_user, tenant_id, installed_app):
|
||||
"""Test that sso_verified access mode apps are skipped in filtering."""
|
||||
api = module.InstalledAppsListApi()
|
||||
method = unwrap(api.get)
|
||||
@@ -200,7 +201,7 @@ class TestInstalledAppsListApi:
|
||||
|
||||
assert len(result["installed_apps"]) == 0
|
||||
|
||||
def test_get_installed_apps_filters_null_apps(self, app, current_user, tenant_id):
|
||||
def test_get_installed_apps_filters_null_apps(self, app: Flask, current_user, tenant_id):
|
||||
"""Test that installed apps with null app are filtered out."""
|
||||
api = module.InstalledAppsListApi()
|
||||
method = unwrap(api.get)
|
||||
@@ -226,7 +227,7 @@ class TestInstalledAppsListApi:
|
||||
|
||||
assert result["installed_apps"] == []
|
||||
|
||||
def test_get_installed_apps_current_tenant_none(self, app, tenant_id, installed_app):
|
||||
def test_get_installed_apps_current_tenant_none(self, app: Flask, tenant_id, installed_app):
|
||||
"""Test error when current_user.current_tenant is None."""
|
||||
api = module.InstalledAppsListApi()
|
||||
method = unwrap(api.get)
|
||||
@@ -247,7 +248,7 @@ class TestInstalledAppsListApi:
|
||||
|
||||
|
||||
class TestInstalledAppsCreateApi:
|
||||
def test_post_success(self, app, tenant_id, payload_patch):
|
||||
def test_post_success(self, app: Flask, tenant_id, payload_patch):
|
||||
api = module.InstalledAppsListApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -276,7 +277,7 @@ class TestInstalledAppsCreateApi:
|
||||
assert result == {"message": "App installed successfully"}
|
||||
assert recommended.install_count == 1
|
||||
|
||||
def test_post_recommended_not_found(self, app, payload_patch):
|
||||
def test_post_recommended_not_found(self, app: Flask, payload_patch):
|
||||
api = module.InstalledAppsListApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -291,7 +292,7 @@ class TestInstalledAppsCreateApi:
|
||||
with pytest.raises(NotFound):
|
||||
method(api)
|
||||
|
||||
def test_post_app_not_public(self, app, tenant_id, payload_patch):
|
||||
def test_post_app_not_public(self, app: Flask, tenant_id, payload_patch):
|
||||
api = module.InstalledAppsListApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -315,7 +316,7 @@ class TestInstalledAppsCreateApi:
|
||||
|
||||
|
||||
class TestInstalledAppApi:
|
||||
def test_delete_success(self, tenant_id, installed_app):
|
||||
def test_delete_success(self, tenant_id: str, installed_app):
|
||||
api = module.InstalledAppApi()
|
||||
method = unwrap(api.delete)
|
||||
|
||||
@@ -328,7 +329,7 @@ class TestInstalledAppApi:
|
||||
assert status == 204
|
||||
assert resp["result"] == "success"
|
||||
|
||||
def test_delete_owned_by_current_tenant(self, tenant_id):
|
||||
def test_delete_owned_by_current_tenant(self, tenant_id: str):
|
||||
api = module.InstalledAppApi()
|
||||
method = unwrap(api.delete)
|
||||
|
||||
@@ -338,7 +339,7 @@ class TestInstalledAppApi:
|
||||
with pytest.raises(BadRequest):
|
||||
method(installed_app)
|
||||
|
||||
def test_patch_update_pin(self, app, payload_patch, installed_app):
|
||||
def test_patch_update_pin(self, app: Flask, payload_patch, installed_app):
|
||||
api = module.InstalledAppApi()
|
||||
method = unwrap(api.patch)
|
||||
|
||||
@@ -352,7 +353,7 @@ class TestInstalledAppApi:
|
||||
assert installed_app.is_pinned is True
|
||||
assert result["result"] == "success"
|
||||
|
||||
def test_patch_no_change(self, app, payload_patch, installed_app):
|
||||
def test_patch_no_change(self, app: Flask, payload_patch, installed_app):
|
||||
api = module.InstalledAppApi()
|
||||
method = unwrap(api.patch)
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ class TestSavedMessageListApi:
|
||||
with pytest.raises(NotCompletionAppError):
|
||||
method(installed_app)
|
||||
|
||||
def test_post_success(self, app, payload_patch):
|
||||
def test_post_success(self, app: Flask, payload_patch):
|
||||
api = module.SavedMessageListApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -102,7 +102,7 @@ class TestSavedMessageListApi:
|
||||
save_mock.assert_called_once()
|
||||
assert result == {"result": "success"}
|
||||
|
||||
def test_post_message_not_exists(self, app, payload_patch):
|
||||
def test_post_message_not_exists(self, app: Flask, payload_patch):
|
||||
api = module.SavedMessageListApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ class TestTrialAppWorkflowRunApi:
|
||||
with pytest.raises(NotWorkflowAppError):
|
||||
method(api, MagicMock(mode=AppMode.CHAT))
|
||||
|
||||
def test_success(self, app, trial_app_workflow, account):
|
||||
def test_success(self, app: Flask, trial_app_workflow, account):
|
||||
api = module.TrialAppWorkflowRunApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -116,7 +116,7 @@ class TestTrialAppWorkflowRunApi:
|
||||
|
||||
assert result is not None
|
||||
|
||||
def test_workflow_provider_not_init(self, app, trial_app_workflow, account):
|
||||
def test_workflow_provider_not_init(self, app: Flask, trial_app_workflow, account):
|
||||
api = module.TrialAppWorkflowRunApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -132,7 +132,7 @@ class TestTrialAppWorkflowRunApi:
|
||||
with pytest.raises(ProviderNotInitializeError):
|
||||
method(api, trial_app_workflow)
|
||||
|
||||
def test_workflow_quota_exceeded(self, app, trial_app_workflow, account):
|
||||
def test_workflow_quota_exceeded(self, app: Flask, trial_app_workflow, account):
|
||||
api = module.TrialAppWorkflowRunApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -148,7 +148,7 @@ class TestTrialAppWorkflowRunApi:
|
||||
with pytest.raises(ProviderQuotaExceededError):
|
||||
method(api, trial_app_workflow)
|
||||
|
||||
def test_workflow_model_not_support(self, app, trial_app_workflow, account):
|
||||
def test_workflow_model_not_support(self, app: Flask, trial_app_workflow, account):
|
||||
api = module.TrialAppWorkflowRunApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -164,7 +164,7 @@ class TestTrialAppWorkflowRunApi:
|
||||
with pytest.raises(ProviderModelCurrentlyNotSupportError):
|
||||
method(api, trial_app_workflow)
|
||||
|
||||
def test_workflow_invoke_error(self, app, trial_app_workflow, account):
|
||||
def test_workflow_invoke_error(self, app: Flask, trial_app_workflow, account):
|
||||
api = module.TrialAppWorkflowRunApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -180,7 +180,7 @@ class TestTrialAppWorkflowRunApi:
|
||||
with pytest.raises(CompletionRequestError):
|
||||
method(api, trial_app_workflow)
|
||||
|
||||
def test_workflow_rate_limit_error(self, app, trial_app_workflow, account):
|
||||
def test_workflow_rate_limit_error(self, app: Flask, trial_app_workflow, account):
|
||||
api = module.TrialAppWorkflowRunApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -196,7 +196,7 @@ class TestTrialAppWorkflowRunApi:
|
||||
with pytest.raises(InvokeRateLimitHttpError):
|
||||
method(api, trial_app_workflow)
|
||||
|
||||
def test_workflow_value_error(self, app, trial_app_workflow, account):
|
||||
def test_workflow_value_error(self, app: Flask, trial_app_workflow, account):
|
||||
api = module.TrialAppWorkflowRunApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -212,7 +212,7 @@ class TestTrialAppWorkflowRunApi:
|
||||
with pytest.raises(ValueError):
|
||||
method(api, trial_app_workflow)
|
||||
|
||||
def test_workflow_generic_exception(self, app, trial_app_workflow, account):
|
||||
def test_workflow_generic_exception(self, app: Flask, trial_app_workflow, account):
|
||||
api = module.TrialAppWorkflowRunApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -238,7 +238,7 @@ class TestTrialChatApi:
|
||||
with pytest.raises(NotChatAppError):
|
||||
method(api, MagicMock(mode="completion"))
|
||||
|
||||
def test_success(self, app, trial_app_chat, account):
|
||||
def test_success(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -252,7 +252,7 @@ class TestTrialChatApi:
|
||||
|
||||
assert result is not None
|
||||
|
||||
def test_chat_conversation_not_exists(self, app, trial_app_chat, account):
|
||||
def test_chat_conversation_not_exists(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -268,7 +268,7 @@ class TestTrialChatApi:
|
||||
with pytest.raises(NotFound):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_chat_conversation_completed(self, app, trial_app_chat, account):
|
||||
def test_chat_conversation_completed(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -284,7 +284,7 @@ class TestTrialChatApi:
|
||||
with pytest.raises(ConversationCompletedError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_chat_app_config_broken(self, app, trial_app_chat, account):
|
||||
def test_chat_app_config_broken(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -300,7 +300,7 @@ class TestTrialChatApi:
|
||||
with pytest.raises(AppUnavailableError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_chat_provider_not_init(self, app, trial_app_chat, account):
|
||||
def test_chat_provider_not_init(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -316,7 +316,7 @@ class TestTrialChatApi:
|
||||
with pytest.raises(ProviderNotInitializeError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_chat_quota_exceeded(self, app, trial_app_chat, account):
|
||||
def test_chat_quota_exceeded(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -332,7 +332,7 @@ class TestTrialChatApi:
|
||||
with pytest.raises(ProviderQuotaExceededError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_chat_model_not_support(self, app, trial_app_chat, account):
|
||||
def test_chat_model_not_support(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -348,7 +348,7 @@ class TestTrialChatApi:
|
||||
with pytest.raises(ProviderModelCurrentlyNotSupportError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_chat_invoke_error(self, app, trial_app_chat, account):
|
||||
def test_chat_invoke_error(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -364,7 +364,7 @@ class TestTrialChatApi:
|
||||
with pytest.raises(CompletionRequestError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_chat_rate_limit_error(self, app, trial_app_chat, account):
|
||||
def test_chat_rate_limit_error(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -380,7 +380,7 @@ class TestTrialChatApi:
|
||||
with pytest.raises(InvokeRateLimitHttpError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_chat_value_error(self, app, trial_app_chat, account):
|
||||
def test_chat_value_error(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -396,7 +396,7 @@ class TestTrialChatApi:
|
||||
with pytest.raises(ValueError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_chat_generic_exception(self, app, trial_app_chat, account):
|
||||
def test_chat_generic_exception(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -422,7 +422,7 @@ class TestTrialCompletionApi:
|
||||
with pytest.raises(NotCompletionAppError):
|
||||
method(api, MagicMock(mode=AppMode.CHAT))
|
||||
|
||||
def test_success(self, app, trial_app_completion, account):
|
||||
def test_success(self, app: Flask, trial_app_completion, account):
|
||||
api = module.TrialCompletionApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -436,7 +436,7 @@ class TestTrialCompletionApi:
|
||||
|
||||
assert result is not None
|
||||
|
||||
def test_completion_app_config_broken(self, app, trial_app_completion, account):
|
||||
def test_completion_app_config_broken(self, app: Flask, trial_app_completion, account):
|
||||
api = module.TrialCompletionApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -452,7 +452,7 @@ class TestTrialCompletionApi:
|
||||
with pytest.raises(AppUnavailableError):
|
||||
method(api, trial_app_completion)
|
||||
|
||||
def test_completion_provider_not_init(self, app, trial_app_completion, account):
|
||||
def test_completion_provider_not_init(self, app: Flask, trial_app_completion, account):
|
||||
api = module.TrialCompletionApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -468,7 +468,7 @@ class TestTrialCompletionApi:
|
||||
with pytest.raises(ProviderNotInitializeError):
|
||||
method(api, trial_app_completion)
|
||||
|
||||
def test_completion_quota_exceeded(self, app, trial_app_completion, account):
|
||||
def test_completion_quota_exceeded(self, app: Flask, trial_app_completion, account):
|
||||
api = module.TrialCompletionApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -484,7 +484,7 @@ class TestTrialCompletionApi:
|
||||
with pytest.raises(ProviderQuotaExceededError):
|
||||
method(api, trial_app_completion)
|
||||
|
||||
def test_completion_model_not_support(self, app, trial_app_completion, account):
|
||||
def test_completion_model_not_support(self, app: Flask, trial_app_completion, account):
|
||||
api = module.TrialCompletionApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -500,7 +500,7 @@ class TestTrialCompletionApi:
|
||||
with pytest.raises(ProviderModelCurrentlyNotSupportError):
|
||||
method(api, trial_app_completion)
|
||||
|
||||
def test_completion_invoke_error(self, app, trial_app_completion, account):
|
||||
def test_completion_invoke_error(self, app: Flask, trial_app_completion, account):
|
||||
api = module.TrialCompletionApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -516,7 +516,7 @@ class TestTrialCompletionApi:
|
||||
with pytest.raises(CompletionRequestError):
|
||||
method(api, trial_app_completion)
|
||||
|
||||
def test_completion_rate_limit_error(self, app, trial_app_completion, account):
|
||||
def test_completion_rate_limit_error(self, app: Flask, trial_app_completion, account):
|
||||
api = module.TrialCompletionApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -532,7 +532,7 @@ class TestTrialCompletionApi:
|
||||
with pytest.raises(InternalServerError):
|
||||
method(api, trial_app_completion)
|
||||
|
||||
def test_completion_value_error(self, app, trial_app_completion, account):
|
||||
def test_completion_value_error(self, app: Flask, trial_app_completion, account):
|
||||
api = module.TrialCompletionApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -548,7 +548,7 @@ class TestTrialCompletionApi:
|
||||
with pytest.raises(ValueError):
|
||||
method(api, trial_app_completion)
|
||||
|
||||
def test_completion_generic_exception(self, app, trial_app_completion, account):
|
||||
def test_completion_generic_exception(self, app: Flask, trial_app_completion, account):
|
||||
api = module.TrialCompletionApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -574,7 +574,7 @@ class TestTrialMessageSuggestedQuestionApi:
|
||||
with pytest.raises(NotChatAppError):
|
||||
method(MagicMock(mode="completion"), str(uuid4()))
|
||||
|
||||
def test_success(self, app, trial_app_chat, account):
|
||||
def test_success(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialMessageSuggestedQuestionApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -591,7 +591,7 @@ class TestTrialMessageSuggestedQuestionApi:
|
||||
|
||||
assert result == {"data": ["q1", "q2"]}
|
||||
|
||||
def test_conversation_not_exists(self, app, trial_app_chat, account):
|
||||
def test_conversation_not_exists(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialMessageSuggestedQuestionApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
@@ -643,7 +643,7 @@ class TestTrialAppParameterApi:
|
||||
|
||||
|
||||
class TestTrialChatAudioApi:
|
||||
def test_success(self, app, trial_app_chat, account):
|
||||
def test_success(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatAudioApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -662,7 +662,7 @@ class TestTrialChatAudioApi:
|
||||
|
||||
assert result == {"text": "hello"}
|
||||
|
||||
def test_app_config_broken(self, app, trial_app_chat, account):
|
||||
def test_app_config_broken(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatAudioApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -683,7 +683,7 @@ class TestTrialChatAudioApi:
|
||||
with pytest.raises(module.AppUnavailableError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_no_audio_uploaded(self, app, trial_app_chat, account):
|
||||
def test_no_audio_uploaded(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatAudioApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -704,7 +704,7 @@ class TestTrialChatAudioApi:
|
||||
with pytest.raises(module.NoAudioUploadedError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_audio_too_large(self, app, trial_app_chat, account):
|
||||
def test_audio_too_large(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatAudioApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -725,7 +725,7 @@ class TestTrialChatAudioApi:
|
||||
with pytest.raises(module.AudioTooLargeError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_unsupported_audio_type(self, app, trial_app_chat, account):
|
||||
def test_unsupported_audio_type(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatAudioApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -746,7 +746,7 @@ class TestTrialChatAudioApi:
|
||||
with pytest.raises(module.UnsupportedAudioTypeError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_provider_not_support_tts(self, app, trial_app_chat, account):
|
||||
def test_provider_not_support_tts(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatAudioApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -767,7 +767,7 @@ class TestTrialChatAudioApi:
|
||||
with pytest.raises(module.ProviderNotSupportSpeechToTextError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_provider_not_init(self, app, trial_app_chat, account):
|
||||
def test_provider_not_init(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatAudioApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -784,7 +784,7 @@ class TestTrialChatAudioApi:
|
||||
with pytest.raises(ProviderNotInitializeError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_quota_exceeded(self, app, trial_app_chat, account):
|
||||
def test_quota_exceeded(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatAudioApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -803,7 +803,7 @@ class TestTrialChatAudioApi:
|
||||
|
||||
|
||||
class TestTrialChatTextApi:
|
||||
def test_success(self, app, trial_app_chat, account):
|
||||
def test_success(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatTextApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -817,7 +817,7 @@ class TestTrialChatTextApi:
|
||||
|
||||
assert result == {"audio": "base64_data"}
|
||||
|
||||
def test_app_config_broken(self, app, trial_app_chat, account):
|
||||
def test_app_config_broken(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatTextApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -833,7 +833,7 @@ class TestTrialChatTextApi:
|
||||
with pytest.raises(module.AppUnavailableError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_provider_not_support(self, app, trial_app_chat, account):
|
||||
def test_provider_not_support(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatTextApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -849,7 +849,7 @@ class TestTrialChatTextApi:
|
||||
with pytest.raises(module.ProviderNotSupportSpeechToTextError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_audio_too_large(self, app, trial_app_chat, account):
|
||||
def test_audio_too_large(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatTextApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -865,7 +865,7 @@ class TestTrialChatTextApi:
|
||||
with pytest.raises(module.AudioTooLargeError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_no_audio_uploaded(self, app, trial_app_chat, account):
|
||||
def test_no_audio_uploaded(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatTextApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -881,7 +881,7 @@ class TestTrialChatTextApi:
|
||||
with pytest.raises(module.NoAudioUploadedError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_provider_not_init(self, app, trial_app_chat, account):
|
||||
def test_provider_not_init(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatTextApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -893,7 +893,7 @@ class TestTrialChatTextApi:
|
||||
with pytest.raises(ProviderNotInitializeError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_quota_exceeded(self, app, trial_app_chat, account):
|
||||
def test_quota_exceeded(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatTextApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -905,7 +905,7 @@ class TestTrialChatTextApi:
|
||||
with pytest.raises(ProviderQuotaExceededError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_model_not_support(self, app, trial_app_chat, account):
|
||||
def test_model_not_support(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatTextApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -917,7 +917,7 @@ class TestTrialChatTextApi:
|
||||
with pytest.raises(ProviderModelCurrentlyNotSupportError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_invoke_error(self, app, trial_app_chat, account):
|
||||
def test_invoke_error(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatTextApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -931,7 +931,7 @@ class TestTrialChatTextApi:
|
||||
|
||||
|
||||
class TestTrialAppWorkflowTaskStopApi:
|
||||
def test_not_workflow_app(self, app, trial_app_chat):
|
||||
def test_not_workflow_app(self, app: Flask, trial_app_chat):
|
||||
api = module.TrialAppWorkflowTaskStopApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -939,7 +939,7 @@ class TestTrialAppWorkflowTaskStopApi:
|
||||
with pytest.raises(NotWorkflowAppError):
|
||||
method(api, trial_app_chat, str(uuid4()))
|
||||
|
||||
def test_success(self, app, trial_app_workflow, account):
|
||||
def test_success(self, app: Flask, trial_app_workflow, account):
|
||||
api = module.TrialAppWorkflowTaskStopApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -1009,7 +1009,7 @@ class TestTrialSitApi:
|
||||
|
||||
|
||||
class TestTrialChatAudioApiExceptionHandlers:
|
||||
def test_provider_not_init(self, app, trial_app_chat, account):
|
||||
def test_provider_not_init(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatAudioApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -1030,7 +1030,7 @@ class TestTrialChatAudioApiExceptionHandlers:
|
||||
with pytest.raises(ProviderNotInitializeError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_quota_exceeded(self, app, trial_app_chat, account):
|
||||
def test_quota_exceeded(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatAudioApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -1051,7 +1051,7 @@ class TestTrialChatAudioApiExceptionHandlers:
|
||||
with pytest.raises(ProviderQuotaExceededError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_invoke_error(self, app, trial_app_chat, account):
|
||||
def test_invoke_error(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatAudioApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -1074,7 +1074,7 @@ class TestTrialChatAudioApiExceptionHandlers:
|
||||
|
||||
|
||||
class TestTrialChatTextApiExceptionHandlers:
|
||||
def test_app_config_broken(self, app, trial_app_chat, account):
|
||||
def test_app_config_broken(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatTextApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -1090,7 +1090,7 @@ class TestTrialChatTextApiExceptionHandlers:
|
||||
with pytest.raises(module.AppUnavailableError):
|
||||
method(api, trial_app_chat)
|
||||
|
||||
def test_unsupported_audio_type(self, app, trial_app_chat, account):
|
||||
def test_unsupported_audio_type(self, app: Flask, trial_app_chat, account):
|
||||
api = module.TrialChatTextApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ def payload():
|
||||
|
||||
|
||||
class TestInstalledAppWorkflowRunApi:
|
||||
def test_not_workflow_app(self, app, non_workflow_installed_app):
|
||||
def test_not_workflow_app(self, app: Flask, non_workflow_installed_app):
|
||||
api = InstalledAppWorkflowRunApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -71,7 +71,7 @@ class TestInstalledAppWorkflowRunApi:
|
||||
with pytest.raises(NotWorkflowAppError):
|
||||
method(non_workflow_installed_app)
|
||||
|
||||
def test_success(self, app, installed_workflow_app, user, payload):
|
||||
def test_success(self, app: Flask, installed_workflow_app, user, payload):
|
||||
api = InstalledAppWorkflowRunApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -91,7 +91,7 @@ class TestInstalledAppWorkflowRunApi:
|
||||
generate_mock.assert_called_once()
|
||||
assert result is not None
|
||||
|
||||
def test_rate_limit_error(self, app, installed_workflow_app, user, payload):
|
||||
def test_rate_limit_error(self, app: Flask, installed_workflow_app, user, payload):
|
||||
api = InstalledAppWorkflowRunApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -109,7 +109,7 @@ class TestInstalledAppWorkflowRunApi:
|
||||
with pytest.raises(InvokeRateLimitHttpError):
|
||||
method(installed_workflow_app)
|
||||
|
||||
def test_unexpected_exception(self, app, installed_workflow_app, user, payload):
|
||||
def test_unexpected_exception(self, app: Flask, installed_workflow_app, user, payload):
|
||||
api = InstalledAppWorkflowRunApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ class TestTagListApi:
|
||||
assert status == 200
|
||||
assert result == [{"id": "1", "name": "tag", "type": "knowledge", "binding_count": "1"}]
|
||||
|
||||
def test_post_success(self, app, admin_user, tag, payload_patch):
|
||||
def test_post_success(self, app: Flask, admin_user, tag, payload_patch):
|
||||
api = TagListApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -144,7 +144,7 @@ class TestTagListApi:
|
||||
|
||||
|
||||
class TestTagUpdateDeleteApi:
|
||||
def test_patch_success(self, app, admin_user, tag, payload_patch):
|
||||
def test_patch_success(self, app: Flask, admin_user, tag, payload_patch):
|
||||
api = TagUpdateDeleteApi()
|
||||
method = unwrap(api.patch)
|
||||
|
||||
@@ -191,7 +191,7 @@ class TestTagUpdateDeleteApi:
|
||||
with pytest.raises(Forbidden):
|
||||
method(api, "tag-1")
|
||||
|
||||
def test_delete_success(self, app, admin_user):
|
||||
def test_delete_success(self, app: Flask, admin_user):
|
||||
api = TagUpdateDeleteApi()
|
||||
method = unwrap(api.delete)
|
||||
|
||||
@@ -210,7 +210,7 @@ class TestTagUpdateDeleteApi:
|
||||
|
||||
|
||||
class TestTagBindingCollectionApi:
|
||||
def test_create_success(self, app, admin_user, payload_patch):
|
||||
def test_create_success(self, app: Flask, admin_user, payload_patch):
|
||||
api = TagBindingCollectionApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
@@ -252,7 +252,7 @@ class TestTagBindingCollectionApi:
|
||||
|
||||
|
||||
class TestTagBindingRemoveApi:
|
||||
def test_remove_success(self, app, admin_user, payload_patch):
|
||||
def test_remove_success(self, app: Flask, admin_user, payload_patch):
|
||||
api = TagBindingRemoveApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ class TestFileApiGet:
|
||||
|
||||
|
||||
class TestFileApiPost:
|
||||
def test_no_file_uploaded(self, app, mock_account_context):
|
||||
def test_no_file_uploaded(self, app: Flask, mock_account_context):
|
||||
api = FileApi()
|
||||
post_method = unwrap(api.post)
|
||||
|
||||
@@ -103,7 +103,7 @@ class TestFileApiPost:
|
||||
with pytest.raises(NoFileUploadedError):
|
||||
post_method(api)
|
||||
|
||||
def test_too_many_files(self, app, mock_account_context):
|
||||
def test_too_many_files(self, app: Flask, mock_account_context):
|
||||
api = FileApi()
|
||||
post_method = unwrap(api.post)
|
||||
|
||||
@@ -120,7 +120,7 @@ class TestFileApiPost:
|
||||
with pytest.raises(TooManyFilesError):
|
||||
post_method(api)
|
||||
|
||||
def test_filename_missing(self, app, mock_account_context):
|
||||
def test_filename_missing(self, app: Flask, mock_account_context):
|
||||
api = FileApi()
|
||||
post_method = unwrap(api.post)
|
||||
|
||||
@@ -132,7 +132,7 @@ class TestFileApiPost:
|
||||
with pytest.raises(FilenameNotExistsError):
|
||||
post_method(api)
|
||||
|
||||
def test_dataset_upload_without_permission(self, app, mock_current_user):
|
||||
def test_dataset_upload_without_permission(self, app: Flask, mock_current_user):
|
||||
mock_current_user.is_dataset_editor = False
|
||||
|
||||
with patch(
|
||||
@@ -151,7 +151,7 @@ class TestFileApiPost:
|
||||
with pytest.raises(Forbidden):
|
||||
post_method(api)
|
||||
|
||||
def test_successful_upload(self, app, mock_account_context, mock_file_service):
|
||||
def test_successful_upload(self, app: Flask, mock_account_context, mock_file_service):
|
||||
api = FileApi()
|
||||
post_method = unwrap(api.post)
|
||||
|
||||
@@ -185,7 +185,7 @@ class TestFileApiPost:
|
||||
assert response["id"] == "file-id-123"
|
||||
assert response["name"] == "test.txt"
|
||||
|
||||
def test_upload_with_invalid_source(self, app, mock_account_context, mock_file_service):
|
||||
def test_upload_with_invalid_source(self, app: Flask, mock_account_context, mock_file_service):
|
||||
"""Test that invalid source parameter gets normalized to None"""
|
||||
api = FileApi()
|
||||
post_method = unwrap(api.post)
|
||||
@@ -225,7 +225,7 @@ class TestFileApiPost:
|
||||
call_kwargs = mock_file_service.upload_file.call_args[1]
|
||||
assert call_kwargs["source"] is None
|
||||
|
||||
def test_file_too_large_error(self, app, mock_account_context, mock_file_service):
|
||||
def test_file_too_large_error(self, app: Flask, mock_account_context, mock_file_service):
|
||||
api = FileApi()
|
||||
post_method = unwrap(api.post)
|
||||
|
||||
@@ -242,7 +242,7 @@ class TestFileApiPost:
|
||||
with pytest.raises(FileTooLargeError):
|
||||
post_method(api)
|
||||
|
||||
def test_unsupported_file_type(self, app, mock_account_context, mock_file_service):
|
||||
def test_unsupported_file_type(self, app: Flask, mock_account_context, mock_file_service):
|
||||
api = FileApi()
|
||||
post_method = unwrap(api.post)
|
||||
|
||||
@@ -259,7 +259,7 @@ class TestFileApiPost:
|
||||
with pytest.raises(UnsupportedFileTypeError):
|
||||
post_method(api)
|
||||
|
||||
def test_blocked_extension(self, app, mock_account_context, mock_file_service):
|
||||
def test_blocked_extension(self, app: Flask, mock_account_context, mock_file_service):
|
||||
api = FileApi()
|
||||
post_method = unwrap(api.post)
|
||||
|
||||
@@ -278,7 +278,7 @@ class TestFileApiPost:
|
||||
|
||||
|
||||
class TestFilePreviewApi:
|
||||
def test_get_preview(self, app, mock_account_context, mock_file_service):
|
||||
def test_get_preview(self, app: Flask, mock_account_context, mock_file_service):
|
||||
api = FilePreviewApi()
|
||||
get_method = unwrap(api.get)
|
||||
mock_file_service.get_file_preview.return_value = "preview text"
|
||||
|
||||
@@ -114,7 +114,7 @@ class TestAccountUpdateApis:
|
||||
(AccountTimezoneApi, {"timezone": "UTC"}),
|
||||
],
|
||||
)
|
||||
def test_update_success(self, app, api_cls, payload):
|
||||
def test_update_success(self, app: Flask, api_cls, payload):
|
||||
api = api_cls()
|
||||
method = unwrap(api.post)
|
||||
|
||||
|
||||
@@ -302,7 +302,7 @@ class TestPluginFetchPermissionApi:
|
||||
|
||||
|
||||
class TestPluginFetchDynamicSelectOptionsApi:
|
||||
def test_fetch_dynamic_options(self, app, user):
|
||||
def test_fetch_dynamic_options(self, app: Flask, user):
|
||||
api = PluginFetchDynamicSelectOptionsApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ from types import SimpleNamespace
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from flask_restx.api import HTTPStatus
|
||||
|
||||
from controllers.service_api.app.annotation import (
|
||||
@@ -163,7 +164,7 @@ class TestAnnotationErrorPatterns:
|
||||
|
||||
|
||||
class TestAnnotationReplyActionApi:
|
||||
def test_enable(self, app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def test_enable(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
enable_mock = Mock()
|
||||
monkeypatch.setattr(AppAnnotationService, "enable_app_annotation", enable_mock)
|
||||
|
||||
@@ -181,7 +182,7 @@ class TestAnnotationReplyActionApi:
|
||||
assert status == 200
|
||||
enable_mock.assert_called_once()
|
||||
|
||||
def test_disable(self, app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def test_disable(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
disable_mock = Mock()
|
||||
monkeypatch.setattr(AppAnnotationService, "disable_app_annotation", disable_mock)
|
||||
|
||||
@@ -231,7 +232,7 @@ class TestAnnotationReplyActionStatusApi:
|
||||
|
||||
|
||||
class TestAnnotationListApi:
|
||||
def test_get(self, app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def test_get(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
annotation = SimpleNamespace(id="a1", question="q", content="a", created_at=0)
|
||||
monkeypatch.setattr(
|
||||
AppAnnotationService,
|
||||
@@ -248,7 +249,7 @@ class TestAnnotationListApi:
|
||||
|
||||
assert response["total"] == 1
|
||||
|
||||
def test_create(self, app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def test_create(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
annotation = SimpleNamespace(id="a1", question="q", content="a", created_at=0)
|
||||
monkeypatch.setattr(
|
||||
AppAnnotationService,
|
||||
@@ -268,7 +269,7 @@ class TestAnnotationListApi:
|
||||
|
||||
|
||||
class TestAnnotationUpdateDeleteApi:
|
||||
def test_update_delete(self, app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def test_update_delete(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
annotation = SimpleNamespace(id="a1", question="q", content="a", created_at=0)
|
||||
monkeypatch.setattr(
|
||||
AppAnnotationService,
|
||||
|
||||
@@ -415,7 +415,7 @@ class TestChatRequestPayloadController:
|
||||
|
||||
|
||||
class TestCompletionApiController:
|
||||
def test_wrong_mode(self, app) -> None:
|
||||
def test_wrong_mode(self, app: Flask) -> None:
|
||||
api = CompletionApi()
|
||||
handler = _unwrap(api.post)
|
||||
app_model = SimpleNamespace(mode=AppMode.CHAT.value)
|
||||
@@ -425,7 +425,7 @@ class TestCompletionApiController:
|
||||
with pytest.raises(AppUnavailableError):
|
||||
handler(api, app_model=app_model, end_user=end_user)
|
||||
|
||||
def test_conversation_not_found(self, app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def test_conversation_not_found(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(
|
||||
AppGenerateService,
|
||||
"generate",
|
||||
@@ -443,7 +443,7 @@ class TestCompletionApiController:
|
||||
|
||||
|
||||
class TestCompletionStopApiController:
|
||||
def test_wrong_mode(self, app) -> None:
|
||||
def test_wrong_mode(self, app: Flask) -> None:
|
||||
api = CompletionStopApi()
|
||||
handler = _unwrap(api.post)
|
||||
app_model = SimpleNamespace(mode=AppMode.CHAT.value)
|
||||
@@ -453,7 +453,7 @@ class TestCompletionStopApiController:
|
||||
with pytest.raises(AppUnavailableError):
|
||||
handler(api, app_model=app_model, end_user=end_user, task_id="t1")
|
||||
|
||||
def test_success(self, app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def test_success(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
stop_mock = Mock()
|
||||
monkeypatch.setattr(AppTaskService, "stop_task", stop_mock)
|
||||
|
||||
@@ -470,7 +470,7 @@ class TestCompletionStopApiController:
|
||||
|
||||
|
||||
class TestChatApiController:
|
||||
def test_wrong_mode(self, app) -> None:
|
||||
def test_wrong_mode(self, app: Flask) -> None:
|
||||
api = ChatApi()
|
||||
handler = _unwrap(api.post)
|
||||
app_model = SimpleNamespace(mode=AppMode.COMPLETION.value)
|
||||
@@ -480,7 +480,7 @@ class TestChatApiController:
|
||||
with pytest.raises(NotChatAppError):
|
||||
handler(api, app_model=app_model, end_user=end_user)
|
||||
|
||||
def test_workflow_not_found(self, app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def test_workflow_not_found(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(
|
||||
AppGenerateService,
|
||||
"generate",
|
||||
@@ -496,7 +496,7 @@ class TestChatApiController:
|
||||
with pytest.raises(NotFound):
|
||||
handler(api, app_model=app_model, end_user=end_user)
|
||||
|
||||
def test_draft_workflow(self, app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def test_draft_workflow(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(
|
||||
AppGenerateService,
|
||||
"generate",
|
||||
@@ -514,10 +514,10 @@ class TestChatApiController:
|
||||
|
||||
|
||||
class TestChatStopApiController:
|
||||
def test_wrong_mode(self, app) -> None:
|
||||
def test_wrong_mode(self, app: Flask) -> None:
|
||||
api = ChatStopApi()
|
||||
handler = _unwrap(api.post)
|
||||
app_model = SimpleNamespace(mode=AppMode.COMPLETION.value)
|
||||
app_model = SimpleNamespace(mode=AppMode.COMPLETION)
|
||||
end_user = SimpleNamespace(id="u1")
|
||||
|
||||
with app.test_request_context("/chat-messages/1/stop", method="POST"):
|
||||
|
||||
@@ -495,7 +495,7 @@ class TestConversationPayloadsController:
|
||||
|
||||
|
||||
class TestConversationApiController:
|
||||
def test_list_not_chat(self, app) -> None:
|
||||
def test_list_not_chat(self, app: Flask) -> None:
|
||||
api = ConversationApi()
|
||||
handler = _unwrap(api.get)
|
||||
app_model = SimpleNamespace(mode=AppMode.COMPLETION)
|
||||
@@ -543,7 +543,7 @@ class TestConversationApiController:
|
||||
|
||||
|
||||
class TestConversationDetailApiController:
|
||||
def test_delete_not_chat(self, app) -> None:
|
||||
def test_delete_not_chat(self, app: Flask) -> None:
|
||||
api = ConversationDetailApi()
|
||||
handler = _unwrap(api.delete)
|
||||
app_model = SimpleNamespace(mode=AppMode.COMPLETION)
|
||||
@@ -593,7 +593,7 @@ class TestConversationRenameApiController:
|
||||
|
||||
|
||||
class TestConversationVariablesApiController:
|
||||
def test_not_chat(self, app) -> None:
|
||||
def test_not_chat(self, app: Flask) -> None:
|
||||
api = ConversationVariablesApi()
|
||||
handler = _unwrap(api.get)
|
||||
app_model = SimpleNamespace(mode=AppMode.COMPLETION)
|
||||
|
||||
@@ -238,7 +238,7 @@ class TestFileApiPost:
|
||||
self,
|
||||
mock_db,
|
||||
mock_file_svc_cls,
|
||||
app,
|
||||
app: Flask,
|
||||
mock_app_model,
|
||||
mock_end_user,
|
||||
):
|
||||
@@ -342,7 +342,7 @@ class TestFileApiPost:
|
||||
self,
|
||||
mock_db,
|
||||
mock_file_svc_cls,
|
||||
app,
|
||||
app: Flask,
|
||||
mock_app_model,
|
||||
mock_end_user,
|
||||
):
|
||||
@@ -374,7 +374,7 @@ class TestFileApiPost:
|
||||
self,
|
||||
mock_db,
|
||||
mock_file_svc_cls,
|
||||
app,
|
||||
app: Flask,
|
||||
mock_app_model,
|
||||
mock_end_user,
|
||||
):
|
||||
|
||||
@@ -66,7 +66,7 @@ class TestFilePreviewApi:
|
||||
return message
|
||||
|
||||
def test_validate_file_ownership_success(
|
||||
self, file_preview_api, mock_app, mock_upload_file, mock_message_file, mock_message
|
||||
self, file_preview_api: FilePreviewApi, mock_app, mock_upload_file, mock_message_file, mock_message
|
||||
):
|
||||
"""Test successful file ownership validation"""
|
||||
file_id = str(uuid.uuid4())
|
||||
@@ -97,7 +97,7 @@ class TestFilePreviewApi:
|
||||
assert result_message_file == mock_message_file
|
||||
assert result_upload_file == mock_upload_file
|
||||
|
||||
def test_validate_file_ownership_file_not_found(self, file_preview_api):
|
||||
def test_validate_file_ownership_file_not_found(self, file_preview_api: FilePreviewApi):
|
||||
"""Test file ownership validation when MessageFile not found"""
|
||||
file_id = str(uuid.uuid4())
|
||||
app_id = str(uuid.uuid4())
|
||||
@@ -112,7 +112,7 @@ class TestFilePreviewApi:
|
||||
|
||||
assert "File not found in message context" in str(exc_info.value)
|
||||
|
||||
def test_validate_file_ownership_access_denied(self, file_preview_api, mock_message_file):
|
||||
def test_validate_file_ownership_access_denied(self, file_preview_api: FilePreviewApi, mock_message_file):
|
||||
"""Test file ownership validation when Message not owned by app"""
|
||||
file_id = str(uuid.uuid4())
|
||||
app_id = str(uuid.uuid4())
|
||||
@@ -130,7 +130,9 @@ class TestFilePreviewApi:
|
||||
|
||||
assert "not owned by requesting app" in str(exc_info.value)
|
||||
|
||||
def test_validate_file_ownership_upload_file_not_found(self, file_preview_api, mock_message_file, mock_message):
|
||||
def test_validate_file_ownership_upload_file_not_found(
|
||||
self, file_preview_api: FilePreviewApi, mock_message_file, mock_message
|
||||
):
|
||||
"""Test file ownership validation when UploadFile not found"""
|
||||
file_id = str(uuid.uuid4())
|
||||
app_id = str(uuid.uuid4())
|
||||
@@ -151,7 +153,7 @@ class TestFilePreviewApi:
|
||||
assert "Upload file record not found" in str(exc_info.value)
|
||||
|
||||
def test_validate_file_ownership_tenant_mismatch(
|
||||
self, file_preview_api, mock_app, mock_upload_file, mock_message_file, mock_message
|
||||
self, file_preview_api: FilePreviewApi, mock_app, mock_upload_file, mock_message_file, mock_message
|
||||
):
|
||||
"""Test file ownership validation with tenant mismatch"""
|
||||
file_id = str(uuid.uuid4())
|
||||
@@ -182,7 +184,7 @@ class TestFilePreviewApi:
|
||||
|
||||
assert "tenant mismatch" in str(exc_info.value)
|
||||
|
||||
def test_validate_file_ownership_invalid_input(self, file_preview_api):
|
||||
def test_validate_file_ownership_invalid_input(self, file_preview_api: FilePreviewApi):
|
||||
"""Test file ownership validation with invalid input"""
|
||||
|
||||
# Test with empty file_id
|
||||
@@ -195,7 +197,7 @@ class TestFilePreviewApi:
|
||||
file_preview_api._validate_file_ownership("file_id", "")
|
||||
assert "Invalid file or app identifier" in str(exc_info.value)
|
||||
|
||||
def test_build_file_response_basic(self, file_preview_api, mock_upload_file):
|
||||
def test_build_file_response_basic(self, file_preview_api: FilePreviewApi, mock_upload_file):
|
||||
"""Test basic file response building"""
|
||||
mock_generator = Mock()
|
||||
|
||||
@@ -207,7 +209,7 @@ class TestFilePreviewApi:
|
||||
assert response.headers["Content-Length"] == str(mock_upload_file.size)
|
||||
assert "Cache-Control" in response.headers
|
||||
|
||||
def test_build_file_response_as_attachment(self, file_preview_api, mock_upload_file):
|
||||
def test_build_file_response_as_attachment(self, file_preview_api: FilePreviewApi, mock_upload_file):
|
||||
"""Test file response building with attachment flag"""
|
||||
mock_generator = Mock()
|
||||
|
||||
@@ -218,7 +220,7 @@ class TestFilePreviewApi:
|
||||
assert mock_upload_file.name in response.headers["Content-Disposition"]
|
||||
assert response.headers["Content-Type"] == "application/octet-stream"
|
||||
|
||||
def test_build_file_response_html_forces_attachment(self, file_preview_api, mock_upload_file):
|
||||
def test_build_file_response_html_forces_attachment(self, file_preview_api: FilePreviewApi, mock_upload_file):
|
||||
"""Test HTML files are forced to download"""
|
||||
mock_generator = Mock()
|
||||
mock_upload_file.mime_type = "text/html"
|
||||
@@ -231,7 +233,7 @@ class TestFilePreviewApi:
|
||||
assert response.headers["Content-Type"] == "application/octet-stream"
|
||||
assert response.headers["X-Content-Type-Options"] == "nosniff"
|
||||
|
||||
def test_build_file_response_audio_video(self, file_preview_api, mock_upload_file):
|
||||
def test_build_file_response_audio_video(self, file_preview_api: FilePreviewApi, mock_upload_file):
|
||||
"""Test file response building for audio/video files"""
|
||||
mock_generator = Mock()
|
||||
mock_upload_file.mime_type = "video/mp4"
|
||||
@@ -241,7 +243,7 @@ class TestFilePreviewApi:
|
||||
# Check Range support for media files
|
||||
assert response.headers["Accept-Ranges"] == "bytes"
|
||||
|
||||
def test_build_file_response_no_size(self, file_preview_api, mock_upload_file):
|
||||
def test_build_file_response_no_size(self, file_preview_api: FilePreviewApi, mock_upload_file):
|
||||
"""Test file response building when size is unknown"""
|
||||
mock_generator = Mock()
|
||||
mock_upload_file.size = 0 # Unknown size
|
||||
@@ -253,7 +255,14 @@ class TestFilePreviewApi:
|
||||
|
||||
@patch("controllers.service_api.app.file_preview.storage")
|
||||
def test_get_method_integration(
|
||||
self, mock_storage, file_preview_api, mock_app, mock_end_user, mock_upload_file, mock_message_file, mock_message
|
||||
self,
|
||||
mock_storage,
|
||||
file_preview_api: FilePreviewApi,
|
||||
mock_app,
|
||||
mock_end_user,
|
||||
mock_upload_file,
|
||||
mock_message_file,
|
||||
mock_message,
|
||||
):
|
||||
"""Test the full GET method integration (without decorator)"""
|
||||
file_id = str(uuid.uuid4())
|
||||
@@ -295,7 +304,13 @@ class TestFilePreviewApi:
|
||||
|
||||
@patch("controllers.service_api.app.file_preview.storage")
|
||||
def test_storage_error_handling(
|
||||
self, mock_storage, file_preview_api, mock_app, mock_upload_file, mock_message_file, mock_message
|
||||
self,
|
||||
mock_storage,
|
||||
file_preview_api: FilePreviewApi,
|
||||
mock_app,
|
||||
mock_upload_file,
|
||||
mock_message_file,
|
||||
mock_message,
|
||||
):
|
||||
"""Test storage error handling in the core logic"""
|
||||
file_id = str(uuid.uuid4())
|
||||
@@ -334,7 +349,7 @@ class TestFilePreviewApi:
|
||||
assert "Storage error" in str(exc_info.value)
|
||||
|
||||
@patch("controllers.service_api.app.file_preview.logger")
|
||||
def test_validate_file_ownership_unexpected_error_logging(self, mock_logger, file_preview_api):
|
||||
def test_validate_file_ownership_unexpected_error_logging(self, mock_logger, file_preview_api: FilePreviewApi):
|
||||
"""Test that unexpected errors are logged properly"""
|
||||
file_id = str(uuid.uuid4())
|
||||
app_id = str(uuid.uuid4())
|
||||
|
||||
@@ -249,7 +249,9 @@ def _build_resumption_context(task_id: str) -> WorkflowResumptionContext:
|
||||
|
||||
class TestHitlServiceApi:
|
||||
# Service API event-stream continuation
|
||||
def test_workflow_events_continue_on_pause_keeps_stream_open(self, app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def test_workflow_events_continue_on_pause_keeps_stream_open(
|
||||
self, app: Flask, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
workflow_run = SimpleNamespace(
|
||||
id="run-1",
|
||||
app_id="app-1",
|
||||
|
||||
@@ -9,6 +9,7 @@ from types import SimpleNamespace
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
from controllers.service_api.app.human_input_form import WorkflowHumanInputFormApi
|
||||
@@ -17,7 +18,7 @@ from tests.unit_tests.controllers.service_api.conftest import _unwrap
|
||||
|
||||
|
||||
class TestWorkflowHumanInputFormApi:
|
||||
def test_get_success(self, app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def test_get_success(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
definition = SimpleNamespace(
|
||||
model_dump=lambda: {
|
||||
"rendered_content": "Rendered form content",
|
||||
@@ -57,7 +58,7 @@ class TestWorkflowHumanInputFormApi:
|
||||
service_mock.get_form_by_token.assert_called_once_with("token-1")
|
||||
service_mock.ensure_form_active.assert_called_once_with(form)
|
||||
|
||||
def test_get_form_not_in_app(self, app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def test_get_form_not_in_app(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
form = SimpleNamespace(
|
||||
app_id="another-app",
|
||||
tenant_id="tenant-1",
|
||||
@@ -87,7 +88,7 @@ class TestWorkflowHumanInputFormApi:
|
||||
],
|
||||
)
|
||||
def test_get_rejects_non_service_api_recipient_types(
|
||||
self, app, monkeypatch: pytest.MonkeyPatch, recipient_type: RecipientType
|
||||
self, app: Flask, monkeypatch: pytest.MonkeyPatch, recipient_type: RecipientType
|
||||
) -> None:
|
||||
form = SimpleNamespace(
|
||||
app_id="app-1",
|
||||
@@ -111,7 +112,7 @@ class TestWorkflowHumanInputFormApi:
|
||||
|
||||
service_mock.ensure_form_active.assert_not_called()
|
||||
|
||||
def test_post_success(self, app, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def test_post_success(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
form = SimpleNamespace(
|
||||
app_id="app-1",
|
||||
tenant_id="tenant-1",
|
||||
@@ -155,7 +156,7 @@ class TestWorkflowHumanInputFormApi:
|
||||
],
|
||||
)
|
||||
def test_post_rejects_non_service_api_recipient_types(
|
||||
self, app, monkeypatch: pytest.MonkeyPatch, recipient_type: RecipientType
|
||||
self, app: Flask, monkeypatch: pytest.MonkeyPatch, recipient_type: RecipientType
|
||||
) -> None:
|
||||
form = SimpleNamespace(
|
||||
app_id="app-1",
|
||||
|
||||
@@ -381,7 +381,7 @@ class TestMessageService:
|
||||
|
||||
|
||||
class TestMessageListApi:
|
||||
def test_not_chat_app(self, app) -> None:
|
||||
def test_not_chat_app(self, app: Flask) -> None:
|
||||
api = MessageListApi()
|
||||
handler = _unwrap(api.get)
|
||||
app_model = SimpleNamespace(mode=AppMode.COMPLETION.value)
|
||||
@@ -467,7 +467,7 @@ class TestAppGetFeedbacksApi:
|
||||
|
||||
|
||||
class TestMessageSuggestedApi:
|
||||
def test_not_chat(self, app) -> None:
|
||||
def test_not_chat(self, app: Flask) -> None:
|
||||
api = MessageSuggestedApi()
|
||||
handler = _unwrap(api.get)
|
||||
app_model = SimpleNamespace(mode=AppMode.COMPLETION.value)
|
||||
|
||||
@@ -32,7 +32,7 @@ def _mock_repo_for_run(monkeypatch: pytest.MonkeyPatch, workflow_run):
|
||||
|
||||
|
||||
class TestWorkflowEventsApi:
|
||||
def test_wrong_app_mode(self, app) -> None:
|
||||
def test_wrong_app_mode(self, app: Flask) -> None:
|
||||
api = WorkflowEventsApi()
|
||||
handler = _unwrap(api.get)
|
||||
app_model = SimpleNamespace(mode=AppMode.CHAT.value)
|
||||
|
||||
@@ -19,6 +19,7 @@ import uuid
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from werkzeug.exceptions import Forbidden, NotFound
|
||||
|
||||
from controllers.service_api.dataset.document import (
|
||||
@@ -550,7 +551,7 @@ class TestDocumentApiGet:
|
||||
@patch("controllers.service_api.dataset.document.DatasetService")
|
||||
@patch("controllers.service_api.dataset.document.DocumentService")
|
||||
def test_get_document_success_with_all_metadata(
|
||||
self, mock_doc_svc, mock_dataset_svc, app, mock_tenant, mock_doc_detail
|
||||
self, mock_doc_svc, mock_dataset_svc, app: Flask, mock_tenant, mock_doc_detail
|
||||
):
|
||||
"""Test successful document retrieval with metadata='all'."""
|
||||
# Arrange
|
||||
@@ -579,7 +580,7 @@ class TestDocumentApiGet:
|
||||
assert "doc_metadata" in response
|
||||
|
||||
@patch("controllers.service_api.dataset.document.DocumentService")
|
||||
def test_get_document_not_found(self, mock_doc_svc, app, mock_tenant):
|
||||
def test_get_document_not_found(self, mock_doc_svc, app: Flask, mock_tenant):
|
||||
"""Test 404 when document is not found."""
|
||||
# Arrange
|
||||
dataset_id = str(uuid.uuid4())
|
||||
@@ -599,7 +600,7 @@ class TestDocumentApiGet:
|
||||
api.get(tenant_id=mock_tenant.id, dataset_id=dataset_id, document_id="nonexistent")
|
||||
|
||||
@patch("controllers.service_api.dataset.document.DocumentService")
|
||||
def test_get_document_forbidden_wrong_tenant(self, mock_doc_svc, app, mock_tenant, mock_doc_detail):
|
||||
def test_get_document_forbidden_wrong_tenant(self, mock_doc_svc, app: Flask, mock_tenant, mock_doc_detail):
|
||||
"""Test 403 when document tenant doesn't match request tenant."""
|
||||
# Arrange
|
||||
dataset_id = str(uuid.uuid4())
|
||||
@@ -620,7 +621,7 @@ class TestDocumentApiGet:
|
||||
api.get(tenant_id=mock_tenant.id, dataset_id=dataset_id, document_id=mock_doc_detail.id)
|
||||
|
||||
@patch("controllers.service_api.dataset.document.DocumentService")
|
||||
def test_get_document_metadata_only(self, mock_doc_svc, app, mock_tenant, mock_doc_detail):
|
||||
def test_get_document_metadata_only(self, mock_doc_svc, app: Flask, mock_tenant, mock_doc_detail):
|
||||
"""Test document retrieval with metadata='only'."""
|
||||
# Arrange
|
||||
dataset_id = str(uuid.uuid4())
|
||||
@@ -647,7 +648,9 @@ class TestDocumentApiGet:
|
||||
|
||||
@patch("controllers.service_api.dataset.document.DatasetService")
|
||||
@patch("controllers.service_api.dataset.document.DocumentService")
|
||||
def test_get_document_metadata_without(self, mock_doc_svc, mock_dataset_svc, app, mock_tenant, mock_doc_detail):
|
||||
def test_get_document_metadata_without(
|
||||
self, mock_doc_svc, mock_dataset_svc, app: Flask, mock_tenant, mock_doc_detail
|
||||
):
|
||||
"""Test document retrieval with metadata='without'."""
|
||||
# Arrange
|
||||
dataset_id = str(uuid.uuid4())
|
||||
@@ -674,7 +677,7 @@ class TestDocumentApiGet:
|
||||
assert "name" in response
|
||||
|
||||
@patch("controllers.service_api.dataset.document.DocumentService")
|
||||
def test_get_document_invalid_metadata_value(self, mock_doc_svc, app, mock_tenant, mock_doc_detail):
|
||||
def test_get_document_invalid_metadata_value(self, mock_doc_svc, app: Flask, mock_tenant, mock_doc_detail):
|
||||
"""Test error when metadata parameter has invalid value."""
|
||||
# Arrange
|
||||
dataset_id = str(uuid.uuid4())
|
||||
@@ -713,7 +716,7 @@ class TestDocumentApiDelete:
|
||||
|
||||
@patch("controllers.service_api.dataset.document.DocumentService")
|
||||
@patch("controllers.service_api.dataset.document.db")
|
||||
def test_delete_document_success(self, mock_db, mock_doc_svc, app, mock_tenant, mock_document):
|
||||
def test_delete_document_success(self, mock_db, mock_doc_svc, app: Flask, mock_tenant, mock_document):
|
||||
"""Test successful document deletion."""
|
||||
# Arrange
|
||||
dataset_id = str(uuid.uuid4())
|
||||
@@ -741,7 +744,7 @@ class TestDocumentApiDelete:
|
||||
|
||||
@patch("controllers.service_api.dataset.document.DocumentService")
|
||||
@patch("controllers.service_api.dataset.document.db")
|
||||
def test_delete_document_not_found(self, mock_db, mock_doc_svc, app, mock_tenant):
|
||||
def test_delete_document_not_found(self, mock_db, mock_doc_svc, app: Flask, mock_tenant):
|
||||
"""Test 404 when document not found."""
|
||||
# Arrange
|
||||
dataset_id = str(uuid.uuid4())
|
||||
@@ -763,7 +766,7 @@ class TestDocumentApiDelete:
|
||||
|
||||
@patch("controllers.service_api.dataset.document.DocumentService")
|
||||
@patch("controllers.service_api.dataset.document.db")
|
||||
def test_delete_document_archived_forbidden(self, mock_db, mock_doc_svc, app, mock_tenant, mock_document):
|
||||
def test_delete_document_archived_forbidden(self, mock_db, mock_doc_svc, app: Flask, mock_tenant, mock_document):
|
||||
"""Test ArchivedDocumentImmutableError when deleting archived document."""
|
||||
# Arrange
|
||||
dataset_id = str(uuid.uuid4())
|
||||
@@ -785,7 +788,7 @@ class TestDocumentApiDelete:
|
||||
|
||||
@patch("controllers.service_api.dataset.document.DocumentService")
|
||||
@patch("controllers.service_api.dataset.document.db")
|
||||
def test_delete_document_dataset_not_found(self, mock_db, mock_doc_svc, app, mock_tenant):
|
||||
def test_delete_document_dataset_not_found(self, mock_db, mock_doc_svc, app: Flask, mock_tenant):
|
||||
"""Test ValueError when dataset not found."""
|
||||
# Arrange
|
||||
dataset_id = str(uuid.uuid4())
|
||||
@@ -808,7 +811,7 @@ class TestDocumentListApi:
|
||||
@patch("controllers.service_api.dataset.document.marshal")
|
||||
@patch("controllers.service_api.dataset.document.DocumentService")
|
||||
@patch("controllers.service_api.dataset.document.db")
|
||||
def test_list_documents_success(self, mock_db, mock_doc_svc, mock_marshal, app, mock_tenant, mock_dataset):
|
||||
def test_list_documents_success(self, mock_db, mock_doc_svc, mock_marshal, app: Flask, mock_tenant, mock_dataset):
|
||||
"""Test successful document list retrieval."""
|
||||
# Arrange
|
||||
mock_db.session.scalar.return_value = mock_dataset
|
||||
@@ -837,7 +840,7 @@ class TestDocumentListApi:
|
||||
assert response["total"] == 2
|
||||
|
||||
@patch("controllers.service_api.dataset.document.db")
|
||||
def test_list_documents_dataset_not_found(self, mock_db, app, mock_tenant, mock_dataset):
|
||||
def test_list_documents_dataset_not_found(self, mock_db, app: Flask, mock_tenant, mock_dataset):
|
||||
"""Test 404 when dataset not found."""
|
||||
# Arrange
|
||||
mock_db.session.scalar.return_value = None
|
||||
@@ -858,7 +861,9 @@ class TestDocumentIndexingStatusApi:
|
||||
@patch("controllers.service_api.dataset.document.marshal")
|
||||
@patch("controllers.service_api.dataset.document.DocumentService")
|
||||
@patch("controllers.service_api.dataset.document.db")
|
||||
def test_get_indexing_status_success(self, mock_db, mock_doc_svc, mock_marshal, app, mock_tenant, mock_dataset):
|
||||
def test_get_indexing_status_success(
|
||||
self, mock_db, mock_doc_svc, mock_marshal, app: Flask, mock_tenant, mock_dataset
|
||||
):
|
||||
"""Test successful indexing status retrieval."""
|
||||
# Arrange
|
||||
batch_id = "batch_123"
|
||||
@@ -894,7 +899,7 @@ class TestDocumentIndexingStatusApi:
|
||||
assert len(response["data"]) == 1
|
||||
|
||||
@patch("controllers.service_api.dataset.document.db")
|
||||
def test_get_indexing_status_dataset_not_found(self, mock_db, app, mock_tenant, mock_dataset):
|
||||
def test_get_indexing_status_dataset_not_found(self, mock_db, app: Flask, mock_tenant, mock_dataset):
|
||||
"""Test 404 when dataset not found."""
|
||||
# Arrange
|
||||
batch_id = "batch_123"
|
||||
@@ -911,7 +916,9 @@ class TestDocumentIndexingStatusApi:
|
||||
|
||||
@patch("controllers.service_api.dataset.document.DocumentService")
|
||||
@patch("controllers.service_api.dataset.document.db")
|
||||
def test_get_indexing_status_documents_not_found(self, mock_db, mock_doc_svc, app, mock_tenant, mock_dataset):
|
||||
def test_get_indexing_status_documents_not_found(
|
||||
self, mock_db, mock_doc_svc, app: Flask, mock_tenant, mock_dataset
|
||||
):
|
||||
"""Test 404 when no documents found for batch."""
|
||||
# Arrange
|
||||
batch_id = "batch_empty"
|
||||
@@ -978,7 +985,7 @@ class TestDocumentAddByTextApi:
|
||||
mock_knowledge_config,
|
||||
mock_doc_svc,
|
||||
mock_marshal,
|
||||
app,
|
||||
app: Flask,
|
||||
mock_tenant,
|
||||
mock_dataset,
|
||||
):
|
||||
@@ -1029,7 +1036,7 @@ class TestDocumentAddByTextApi:
|
||||
@patch("controllers.service_api.wraps.validate_and_get_api_token")
|
||||
@patch("controllers.service_api.dataset.document.db")
|
||||
def test_create_document_dataset_not_found(
|
||||
self, mock_db, mock_validate_token, mock_feature_svc, app, mock_tenant, mock_dataset
|
||||
self, mock_db, mock_validate_token, mock_feature_svc, app: Flask, mock_tenant, mock_dataset
|
||||
):
|
||||
"""Test ValueError when dataset not found."""
|
||||
# Arrange — neutralise billing decorators
|
||||
@@ -1052,7 +1059,7 @@ class TestDocumentAddByTextApi:
|
||||
@patch("controllers.service_api.wraps.validate_and_get_api_token")
|
||||
@patch("controllers.service_api.dataset.document.db")
|
||||
def test_create_document_missing_indexing_technique(
|
||||
self, mock_db, mock_validate_token, mock_feature_svc, app, mock_tenant, mock_dataset
|
||||
self, mock_db, mock_validate_token, mock_feature_svc, app: Flask, mock_tenant, mock_dataset
|
||||
):
|
||||
"""Test error when both dataset and payload lack indexing_technique.
|
||||
|
||||
@@ -1161,7 +1168,7 @@ class TestDocumentUpdateByTextApiPost:
|
||||
mock_file_svc_cls,
|
||||
mock_doc_svc,
|
||||
mock_marshal,
|
||||
app,
|
||||
app: Flask,
|
||||
mock_tenant,
|
||||
mock_dataset,
|
||||
):
|
||||
@@ -1206,7 +1213,7 @@ class TestDocumentUpdateByTextApiPost:
|
||||
mock_validate_token,
|
||||
mock_feature_svc,
|
||||
mock_db,
|
||||
app,
|
||||
app: Flask,
|
||||
mock_tenant,
|
||||
mock_dataset,
|
||||
):
|
||||
@@ -1245,7 +1252,7 @@ class TestDocumentAddByFileApiPost:
|
||||
mock_validate_token,
|
||||
mock_feature_svc,
|
||||
mock_db,
|
||||
app,
|
||||
app: Flask,
|
||||
mock_tenant,
|
||||
mock_dataset,
|
||||
):
|
||||
@@ -1275,7 +1282,7 @@ class TestDocumentAddByFileApiPost:
|
||||
mock_validate_token,
|
||||
mock_feature_svc,
|
||||
mock_db,
|
||||
app,
|
||||
app: Flask,
|
||||
mock_tenant,
|
||||
mock_dataset,
|
||||
):
|
||||
@@ -1306,7 +1313,7 @@ class TestDocumentAddByFileApiPost:
|
||||
mock_validate_token,
|
||||
mock_feature_svc,
|
||||
mock_db,
|
||||
app,
|
||||
app: Flask,
|
||||
mock_tenant,
|
||||
mock_dataset,
|
||||
):
|
||||
@@ -1338,7 +1345,7 @@ class TestDocumentAddByFileApiPost:
|
||||
mock_validate_token,
|
||||
mock_feature_svc,
|
||||
mock_db,
|
||||
app,
|
||||
app: Flask,
|
||||
mock_tenant,
|
||||
mock_dataset,
|
||||
):
|
||||
@@ -1381,7 +1388,7 @@ class TestDocumentUpdateByFileApiPatch:
|
||||
mock_feature_svc,
|
||||
mock_update_document_by_file,
|
||||
route_name,
|
||||
app,
|
||||
app: Flask,
|
||||
mock_tenant,
|
||||
mock_dataset,
|
||||
):
|
||||
@@ -1418,7 +1425,7 @@ class TestDocumentUpdateByFileApiPatch:
|
||||
mock_validate_token,
|
||||
mock_feature_svc,
|
||||
mock_db,
|
||||
app,
|
||||
app: Flask,
|
||||
mock_tenant,
|
||||
mock_dataset,
|
||||
):
|
||||
@@ -1453,7 +1460,7 @@ class TestDocumentUpdateByFileApiPatch:
|
||||
mock_validate_token,
|
||||
mock_feature_svc,
|
||||
mock_db,
|
||||
app,
|
||||
app: Flask,
|
||||
mock_tenant,
|
||||
mock_dataset,
|
||||
):
|
||||
@@ -1497,7 +1504,7 @@ class TestDocumentUpdateByFileApiPatch:
|
||||
mock_file_svc_cls,
|
||||
mock_doc_svc,
|
||||
mock_marshal,
|
||||
app,
|
||||
app: Flask,
|
||||
mock_tenant,
|
||||
mock_dataset,
|
||||
):
|
||||
|
||||
@@ -18,6 +18,7 @@ import uuid
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from werkzeug.exceptions import Forbidden, NotFound
|
||||
|
||||
import services
|
||||
@@ -91,7 +92,7 @@ class TestHitTestingApiPost:
|
||||
mock_hit_svc,
|
||||
mock_marshal,
|
||||
mock_ns,
|
||||
app,
|
||||
app: Flask,
|
||||
):
|
||||
"""Test successful hit testing request."""
|
||||
dataset_id = str(uuid.uuid4())
|
||||
@@ -129,7 +130,7 @@ class TestHitTestingApiPost:
|
||||
mock_hit_svc,
|
||||
mock_marshal,
|
||||
mock_ns,
|
||||
app,
|
||||
app: Flask,
|
||||
):
|
||||
"""Test hit testing with custom retrieval model."""
|
||||
dataset_id = str(uuid.uuid4())
|
||||
@@ -183,7 +184,7 @@ class TestHitTestingApiPost:
|
||||
mock_hit_svc,
|
||||
mock_marshal,
|
||||
mock_ns,
|
||||
app,
|
||||
app: Flask,
|
||||
):
|
||||
"""Service API retrieval payload should not drop metadata filters."""
|
||||
dataset_id = str(uuid.uuid4())
|
||||
@@ -239,7 +240,7 @@ class TestHitTestingApiPost:
|
||||
mock_hit_svc,
|
||||
mock_marshal,
|
||||
mock_ns,
|
||||
app,
|
||||
app: Flask,
|
||||
):
|
||||
"""Test service API prepares nullable list fields from marshalled records."""
|
||||
dataset_id = str(uuid.uuid4())
|
||||
@@ -286,7 +287,7 @@ class TestHitTestingApiPost:
|
||||
mock_current_user,
|
||||
mock_dataset_svc,
|
||||
mock_ns,
|
||||
app,
|
||||
app: Flask,
|
||||
):
|
||||
"""Test hit testing with non-existent dataset."""
|
||||
dataset_id = str(uuid.uuid4())
|
||||
@@ -308,7 +309,7 @@ class TestHitTestingApiPost:
|
||||
mock_current_user,
|
||||
mock_dataset_svc,
|
||||
mock_ns,
|
||||
app,
|
||||
app: Flask,
|
||||
):
|
||||
"""Test hit testing when user lacks dataset permission."""
|
||||
dataset_id = str(uuid.uuid4())
|
||||
|
||||
@@ -54,7 +54,7 @@ class TestIndexApi:
|
||||
assert isinstance(response["server_version"], str)
|
||||
|
||||
@pytest.mark.parametrize("version", ["0.0.1", "1.0.0", "2.0.0-beta", "1.11.4"])
|
||||
def test_get_returns_correct_version(self, app, version):
|
||||
def test_get_returns_correct_version(self, app: Flask, version):
|
||||
"""Test that server_version matches config version."""
|
||||
# Arrange
|
||||
mock_config = MagicMock()
|
||||
|
||||
@@ -44,7 +44,7 @@ class TestEmailCodeLoginSendEmailApi:
|
||||
self,
|
||||
mock_get_user,
|
||||
mock_send_email,
|
||||
app,
|
||||
app: Flask,
|
||||
):
|
||||
mock_account = MagicMock()
|
||||
mock_get_user.return_value = mock_account
|
||||
@@ -75,7 +75,7 @@ class TestEmailCodeLoginApi:
|
||||
mock_get_user,
|
||||
mock_login,
|
||||
mock_reset_login_rate,
|
||||
app,
|
||||
app: Flask,
|
||||
):
|
||||
mock_get_token_data.return_value = {"email": "User@Example.com", "code": "123456"}
|
||||
mock_get_user.return_value = MagicMock()
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
|
||||
Snapshot generated from `packages/contracts/generated/api/readiness.json` after running `pnpm -C packages/contracts gen-api-contract-from-openapi`.
|
||||
|
||||
Are we OpenAPI ready? **No.** Current generated API contracts are **16.6% ready**.
|
||||
Are we OpenAPI ready? **No.** Current generated API contracts are **16.7% ready**.
|
||||
|
||||
| Surface | Ready | Not ready | Total | Ready % |
|
||||
| --------- | ------: | --------: | ------: | --------: |
|
||||
| console | 95 | 475 | 570 | 16.7% |
|
||||
| console | 96 | 474 | 570 | 16.8% |
|
||||
| service | 16 | 72 | 88 | 18.2% |
|
||||
| web | 5 | 36 | 41 | 12.2% |
|
||||
| **total** | **116** | **583** | **699** | **16.6%** |
|
||||
| **total** | **117** | **582** | **699** | **16.7%** |
|
||||
|
||||
Readiness here means the generated contract operation is not marked with:
|
||||
|
||||
|
||||
@@ -426,16 +426,10 @@ export const imports = {
|
||||
|
||||
/**
|
||||
* Get workflow online users
|
||||
*
|
||||
* Generated contract types may be inaccurate because backend OpenAPI annotations are incomplete. Do not migrate callers until the generated contract is accurate.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
export const post3 = oc
|
||||
.route({
|
||||
deprecated: true,
|
||||
description:
|
||||
'Get workflow online users\n\nGenerated contract types may be inaccurate because backend OpenAPI annotations are incomplete. Do not migrate callers until the generated contract is accurate.',
|
||||
description: 'Get workflow online users',
|
||||
inputStructure: 'detailed',
|
||||
method: 'POST',
|
||||
operationId: 'postAppsWorkflowsOnlineUsers',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"surfaces": {
|
||||
"console": {
|
||||
"notReady": 475,
|
||||
"notReady": 474,
|
||||
"total": 570
|
||||
},
|
||||
"service": {
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
@reference "../../../../styles/globals.css";
|
||||
|
||||
.modal {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.modalHeader {
|
||||
@apply flex items-center place-content-between h-8;
|
||||
}
|
||||
@@ -19,7 +15,7 @@
|
||||
background-size: 16px;
|
||||
}
|
||||
|
||||
.modal .tip {
|
||||
.tip {
|
||||
@apply mt-1 mb-8 text-text-tertiary;
|
||||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
|
||||
@@ -53,7 +53,7 @@ const EmptyDatasetCreationModal = ({ show = false, onHide }: IProps) => {
|
||||
onHide()
|
||||
}}
|
||||
>
|
||||
<DialogContent className={cn('w-full overflow-hidden! border-none text-left align-middle', cn(s.modal, '!max-w-[520px]', 'px-8'))}>
|
||||
<DialogContent className="w-full max-w-[520px]! overflow-hidden! border-none px-8 text-left align-middle">
|
||||
|
||||
<div className={s.modalHeader}>
|
||||
<div className={s.title}>{t('stepOne.modal.title', { ns: 'datasetCreation' })}</div>
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
@reference "../../../../styles/globals.css";
|
||||
|
||||
.modal {
|
||||
position: relative;
|
||||
}
|
||||
.modal .icon {
|
||||
.icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: rgba(255, 255, 255, 0.9) center no-repeat url(../assets/annotation-info.svg);
|
||||
@@ -12,7 +9,7 @@
|
||||
box-shadow: 0px 20px 24px -4px rgba(16, 24, 40, 0.08), 0px 8px 8px -4px rgba(16, 24, 40, 0.03);
|
||||
border-radius: 12px;
|
||||
}
|
||||
.modal .close {
|
||||
.close {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
@@ -23,14 +20,14 @@
|
||||
background-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.modal .title {
|
||||
.title {
|
||||
@apply mt-3 mb-1;
|
||||
font-weight: 600;
|
||||
font-size: 20px;
|
||||
line-height: 30px;
|
||||
color: #101828;
|
||||
}
|
||||
.modal .content {
|
||||
.content {
|
||||
@apply mb-10;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
|
||||
@@ -39,7 +39,7 @@ const StopEmbeddingModal = ({
|
||||
onHide()
|
||||
}}
|
||||
>
|
||||
<AlertDialogContent className={cn(s.modal, 'max-w-[480px]! overflow-hidden! border-none px-8 py-6 text-left align-middle shadow-xl')}>
|
||||
<AlertDialogContent className="max-w-[480px]! overflow-hidden! border-none px-8 py-6 text-left align-middle shadow-xl">
|
||||
<div className={s.icon} />
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user