- 新增多个集成服务(Slack、Notion、Twilio、Microsoft365等)的元数据、常量、凭证、错误处理相关单元测试 - 新增持久化适配器、工作单元、健康检查仓储的单元测试 - 修复认证插件测试中缺失的凭证类型注册 - 修正密钥轮换调度器的异常类型匹配 - 完善审计日志测试用例,新增创建者/更新者字段校验 - 新增告警触发时持久化通知渠道的测试 - 精简核心模型测试代码,移除冗余的超时测试用例
214 lines
6.8 KiB
Python
214 lines
6.8 KiB
Python
"""integrations/schemas.py 数据结构单元测试。
|
|
|
|
覆盖 IntegrationMetadata 与 GeneratedToolsDraft 两个 Pydantic BaseModel
|
|
的字段声明、默认值、约束校验与 extra="forbid" 行为。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from yuxi.external_systems.integrations.schemas import (
|
|
GeneratedToolsDraft,
|
|
IntegrationMetadata,
|
|
)
|
|
|
|
|
|
# ─── 辅助构造 ─────────────────────────────────────────────────────────────
|
|
|
|
|
|
def _metadata_kwargs(**overrides: Any) -> dict[str, Any]:
|
|
"""构造 IntegrationMetadata 最小合法字段,按需覆盖。"""
|
|
base: dict[str, Any] = {
|
|
"key": "salesforce",
|
|
"display_name": "Salesforce",
|
|
"adapter_type": "http",
|
|
"source_types": ["salesforce"],
|
|
}
|
|
base.update(overrides)
|
|
return base
|
|
|
|
|
|
# ─── IntegrationMetadata 字段声明 ─────────────────────────────────────────
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_integrationMetadata_fields_matchExpected():
|
|
# Arrange / Act
|
|
meta = IntegrationMetadata(
|
|
key="hubspot",
|
|
display_name="HubSpot",
|
|
description="CRM integration",
|
|
adapter_type="http",
|
|
source_types=["hubspot"],
|
|
supported_auth_types=["oauth2"],
|
|
tags=["crm", "marketing"],
|
|
icon="hubspot",
|
|
docs_url="https://docs.hubspot.com",
|
|
connection_extra_schema={"type": "object"},
|
|
example_config={"host": "api.hubspot.com"},
|
|
dependency_note="需要 OAuth2 应用",
|
|
)
|
|
|
|
# Assert
|
|
assert meta.key == "hubspot"
|
|
assert meta.display_name == "HubSpot"
|
|
assert meta.description == "CRM integration"
|
|
assert meta.adapter_type == "http"
|
|
assert meta.source_types == ["hubspot"]
|
|
assert meta.supported_auth_types == ["oauth2"]
|
|
assert meta.tags == ["crm", "marketing"]
|
|
assert meta.icon == "hubspot"
|
|
assert meta.docs_url == "https://docs.hubspot.com"
|
|
assert meta.connection_extra_schema == {"type": "object"}
|
|
assert meta.example_config == {"host": "api.hubspot.com"}
|
|
assert meta.dependency_note == "需要 OAuth2 应用"
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_integrationMetadata_defaults_appliedWhenOptionalOmitted():
|
|
# Arrange / Act - 仅传必填字段
|
|
meta = IntegrationMetadata(**_metadata_kwargs())
|
|
|
|
# Assert - 可选字段取默认值
|
|
assert meta.description == ""
|
|
assert meta.supported_auth_types == []
|
|
assert meta.tags == []
|
|
assert meta.icon is None
|
|
assert meta.docs_url is None
|
|
assert meta.connection_extra_schema is None
|
|
assert meta.example_config is None
|
|
assert meta.dependency_note is None
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_integrationMetadata_extraField_forbidden_raises():
|
|
# Arrange / Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
IntegrationMetadata(**_metadata_kwargs(unknown_field="x"))
|
|
assert "extra" in str(exc_info.value).lower()
|
|
|
|
|
|
# ─── IntegrationMetadata 约束校验 ──────────────────────────────────────────
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_integrationMetadata_key_empty_raises():
|
|
# Arrange / Act / Assert - min_length=1
|
|
with pytest.raises(ValidationError):
|
|
IntegrationMetadata(**_metadata_kwargs(key=""))
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_integrationMetadata_key_exceeds_maxLength_raises():
|
|
# Arrange / Act / Assert - max_length=64
|
|
with pytest.raises(ValidationError):
|
|
IntegrationMetadata(**_metadata_kwargs(key="a" * 65))
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_integrationMetadata_key_maxLength_boundary_accepted():
|
|
# Arrange / Act - 边界值 64 字符应通过
|
|
meta = IntegrationMetadata(**_metadata_kwargs(key="a" * 64))
|
|
|
|
# Assert
|
|
assert len(meta.key) == 64
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_integrationMetadata_displayName_empty_raises():
|
|
# Arrange / Act / Assert - min_length=1
|
|
with pytest.raises(ValidationError):
|
|
IntegrationMetadata(**_metadata_kwargs(display_name=""))
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_integrationMetadata_displayName_exceeds_maxLength_raises():
|
|
# Arrange / Act / Assert - max_length=128
|
|
with pytest.raises(ValidationError):
|
|
IntegrationMetadata(**_metadata_kwargs(display_name="a" * 129))
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_integrationMetadata_description_exceeds_maxLength_raises():
|
|
# Arrange / Act / Assert - max_length=512
|
|
with pytest.raises(ValidationError):
|
|
IntegrationMetadata(**_metadata_kwargs(description="a" * 513))
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_integrationMetadata_adapterType_exceeds_maxLength_raises():
|
|
# Arrange / Act / Assert - max_length=32
|
|
with pytest.raises(ValidationError):
|
|
IntegrationMetadata(**_metadata_kwargs(adapter_type="a" * 33))
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_integrationMetadata_sourceTypes_empty_raises():
|
|
# Arrange / Act / Assert - min_length=1
|
|
with pytest.raises(ValidationError):
|
|
IntegrationMetadata(**_metadata_kwargs(source_types=[]))
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_integrationMetadata_missing_required_field_raises():
|
|
# Arrange / Act / Assert - 缺少 key
|
|
with pytest.raises(ValidationError):
|
|
IntegrationMetadata(
|
|
display_name="x",
|
|
adapter_type="http",
|
|
source_types=["x"],
|
|
)
|
|
|
|
|
|
# ─── GeneratedToolsDraft 字段声明 ─────────────────────────────────────────
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_generatedToolsDraft_fields_matchExpected():
|
|
# Arrange / Act
|
|
draft = GeneratedToolsDraft(
|
|
tool_configs=[{"slug": "tool-a"}, {"slug": "tool-b"}],
|
|
override_existing=True,
|
|
)
|
|
|
|
# Assert
|
|
assert draft.tool_configs == [{"slug": "tool-a"}, {"slug": "tool-b"}]
|
|
assert draft.override_existing is True
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_generatedToolsDraft_overrideExisting_defaultsFalse():
|
|
# Arrange / Act - 仅传必填字段
|
|
draft = GeneratedToolsDraft(tool_configs=[{"slug": "tool-a"}])
|
|
|
|
# Assert
|
|
assert draft.override_existing is False
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_generatedToolsDraft_toolConfigs_required_raises():
|
|
# Arrange / Act / Assert - 缺少 tool_configs
|
|
with pytest.raises(ValidationError):
|
|
GeneratedToolsDraft() # type: ignore[call-arg]
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_generatedToolsDraft_extraField_forbidden_raises():
|
|
# Arrange / Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
GeneratedToolsDraft(tool_configs=[], unknown="x")
|
|
assert "extra" in str(exc_info.value).lower()
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_generatedToolsDraft_emptyToolConfigs_accepted():
|
|
# Arrange / Act - 空列表合法(无 min_length 约束)
|
|
draft = GeneratedToolsDraft(tool_configs=[])
|
|
|
|
# Assert
|
|
assert draft.tool_configs == []
|