Files
xiaoxia-saas/tests/unit/test_sms_service.py
T
CI Bot 47fee9412e
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 28s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 57s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 37s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m29s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m30s
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 34s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m59s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 2m28s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m34s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 4m11s
AI Code Review / AI Code Review (pull_request) Successful in 4m24s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 6m12s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 3m2s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m41s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 45s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 1286h55m52s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 1287h7m5s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 1287h7m9s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 1287h7m11s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 1287h17m41s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 1287h17m45s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 1287h17m47s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 1287h17m51s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 1287h22m53s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 1287h22m57s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 1287h39m37s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 1287h50m19s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 1287h55m25s
test(unit): 第62波 - module_registry + asr_service_factory + sms_service
- test_module_registry: 模块注册中心,38个用例
  - Module/ModuleCapability/QuotaRule 数据类
  - 注册/注销/查询/依赖检查
  - 能力发现与配额规则
  - 全局单例清理

- test_asr_service_factory: ASR服务工厂,8个用例
  - 默认/空值/空白 provider 返回 None
  - mock provider 返回 MockASRService
  - 未知 provider 降级返回 None
  - 单例缓存 + reset 缓存

- test_sms_service: 短信服务,12个用例
  - NoopSmsService 空实现
  - AliyunSmsService 初始化(env + 显式参数)
  - 验证码委托到模板短信
  - get_sms_service 工厂(noop/aliyun/未知/大小写)

+58
2026-07-25 00:04:19 +08:00

158 lines
5.8 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""SMS 短信服务单元测试."""
from __future__ import annotations
import os
import pytest
from packages.adapters.sms.sms_service import (
AliyunSmsService,
NoopSmsService,
get_sms_service,
)
class TestNoopSmsService:
"""NoopSmsService 空实现测试."""
def test_send_verification_code_returns_true(self):
"""发送验证码返回True."""
svc = NoopSmsService()
result = svc.send_verification_code("13800138000", "123456")
assert result is True
def test_send_template_sms_returns_true(self):
"""发送模板短信返回True."""
svc = NoopSmsService()
result = svc.send_template_sms(
"13800138000",
"SMS_123456",
{"code": "123456"},
)
assert result is True
def test_send_verification_code_empty_code(self):
"""空验证码也返回True(空实现不做校验)."""
svc = NoopSmsService()
result = svc.send_verification_code("13800138000", "")
assert result is True
def test_send_template_sms_empty_params(self):
"""空参数也返回True."""
svc = NoopSmsService()
result = svc.send_template_sms("13800138000", "TPL_001", {})
assert result is True
class TestAliyunSmsServiceInit:
"""AliyunSmsService 初始化测试."""
def test_default_config_from_env(self, monkeypatch):
"""默认从环境变量读取配置."""
monkeypatch.setenv("ALIYUN_SMS_ACCESS_KEY_ID", "test_key")
monkeypatch.setenv("ALIYUN_SMS_ACCESS_KEY_SECRET", "test_secret")
monkeypatch.setenv("ALIYUN_SMS_SIGN_NAME", "测试签名")
monkeypatch.setenv("ALIYUN_SMS_VERIFY_TEMPLATE_ID", "SMS_TEST_001")
svc = AliyunSmsService()
assert svc.access_key_id == "test_key"
assert svc.access_key_secret == "test_secret"
assert svc.sign_name == "测试签名"
assert svc.verify_template_id == "SMS_TEST_001"
def test_explicit_config_overrides_env(self, monkeypatch):
"""显式参数覆盖环境变量."""
monkeypatch.setenv("ALIYUN_SMS_ACCESS_KEY_ID", "env_key")
svc = AliyunSmsService(access_key_id="explicit_key")
assert svc.access_key_id == "explicit_key"
def test_default_values_when_no_env(self, monkeypatch):
"""无环境变量时使用默认值."""
for key in [
"ALIYUN_SMS_ACCESS_KEY_ID",
"ALIYUN_SMS_ACCESS_KEY_SECRET",
"ALIYUN_SMS_SIGN_NAME",
"ALIYUN_SMS_VERIFY_TEMPLATE_ID",
]:
monkeypatch.delenv(key, raising=False)
svc = AliyunSmsService()
assert svc.access_key_id == ""
assert svc.access_key_secret == ""
assert svc.sign_name == "小应剪辑"
assert svc.verify_template_id == "SMS_123456789"
def test_send_verification_code_delegates_to_template(self):
"""send_verification_code 委托给 send_template_sms."""
svc = AliyunSmsService(
access_key_id="key",
access_key_secret="secret",
verify_template_id="SMS_VERIFY",
)
called_with = {}
def mock_template_sms(phone, template_id, params):
called_with["phone"] = phone
called_with["template_id"] = template_id
called_with["params"] = params
return True
svc.send_template_sms = mock_template_sms
result = svc.send_verification_code("13800138000", "654321")
assert result is True
assert called_with["phone"] == "13800138000"
assert called_with["template_id"] == "SMS_VERIFY"
assert called_with["params"] == {"code": "654321"}
def test_send_template_sms_import_error_returns_false(self):
"""SDK未安装时返回False(ImportError路径)."""
svc = AliyunSmsService(access_key_id="k", access_key_secret="s")
# 没有安装SDK时会返回False
# 由于测试环境可能安装了SDK,这里不强制断言具体结果
# 只验证函数不会抛异常
try:
result = svc.send_template_sms("13800138000", "TPL_001", {"code": "123"})
assert isinstance(result, bool)
except Exception as e:
# SDK可用时可能因为凭证无效而返回False,不应抛未预期的异常
pytest.fail(f"Unexpected exception: {e}")
class TestGetSmsService:
"""短信服务工厂函数测试."""
def test_default_noop(self, monkeypatch):
"""默认使用NoopSmsService."""
monkeypatch.delenv("SMS_PROVIDER", raising=False)
svc = get_sms_service()
assert isinstance(svc, NoopSmsService)
def test_noop_provider(self, monkeypatch):
"""显式指定noop provider."""
monkeypatch.setenv("SMS_PROVIDER", "noop")
svc = get_sms_service()
assert isinstance(svc, NoopSmsService)
def test_aliyun_provider(self, monkeypatch):
"""指定aliyun provider返回AliyunSmsService."""
monkeypatch.setenv("SMS_PROVIDER", "aliyun")
monkeypatch.setenv("ALIYUN_SMS_ACCESS_KEY_ID", "k")
monkeypatch.setenv("ALIYUN_SMS_ACCESS_KEY_SECRET", "s")
svc = get_sms_service()
assert isinstance(svc, AliyunSmsService)
def test_unknown_provider_falls_back_to_noop(self, monkeypatch):
"""未知provider回退到NoopSmsService."""
monkeypatch.setenv("SMS_PROVIDER", "unknown_provider_xyz")
svc = get_sms_service()
assert isinstance(svc, NoopSmsService)
def test_provider_case_insensitive(self, monkeypatch):
"""provider大小写不敏感."""
monkeypatch.setenv("SMS_PROVIDER", "ALIYUN")
monkeypatch.setenv("ALIYUN_SMS_ACCESS_KEY_ID", "k")
monkeypatch.setenv("ALIYUN_SMS_ACCESS_KEY_SECRET", "s")
svc = get_sms_service()
assert isinstance(svc, AliyunSmsService)