main:添加核心文件并初始化项目
新增内容: - 创建基础项目结构。 - 添加 `.gitignore` 和 `.dockerignore` 文件。 - 编写 `pyproject.toml` 和依赖文件。 - 添加算法模块及示例算法。 - 实现核心功能模块(日志、错误处理、指标)。 - 添加开发和运行所需的相关脚本文件及文档。
This commit is contained in:
1
tests/__init__.py
Normal file
1
tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""测试模块"""
|
||||
23
tests/conftest.py
Normal file
23
tests/conftest.py
Normal file
@@ -0,0 +1,23 @@
|
||||
"""pytest 配置"""
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from src.functional_scaffold.main import app
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
"""测试客户端"""
|
||||
return TestClient(app)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_prime_numbers():
|
||||
"""质数样本"""
|
||||
return [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_composite_numbers():
|
||||
"""合数样本"""
|
||||
return [4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 22, 24, 25]
|
||||
77
tests/test_algorithms.py
Normal file
77
tests/test_algorithms.py
Normal file
@@ -0,0 +1,77 @@
|
||||
"""算法单元测试"""
|
||||
|
||||
import pytest
|
||||
from src.functional_scaffold.algorithms.prime_checker import PrimeChecker
|
||||
|
||||
|
||||
class TestPrimeChecker:
|
||||
"""质数判断算法测试"""
|
||||
|
||||
def setup_method(self):
|
||||
"""每个测试方法前执行"""
|
||||
self.checker = PrimeChecker()
|
||||
|
||||
def test_prime_numbers(self, sample_prime_numbers):
|
||||
"""测试质数判断"""
|
||||
for num in sample_prime_numbers:
|
||||
result = self.checker.process(num)
|
||||
assert result["is_prime"] is True
|
||||
assert result["number"] == num
|
||||
assert result["factors"] == []
|
||||
assert result["algorithm"] == "trial_division"
|
||||
|
||||
def test_composite_numbers(self, sample_composite_numbers):
|
||||
"""测试合数判断"""
|
||||
for num in sample_composite_numbers:
|
||||
result = self.checker.process(num)
|
||||
assert result["is_prime"] is False
|
||||
assert result["number"] == num
|
||||
assert len(result["factors"]) > 0
|
||||
assert result["algorithm"] == "trial_division"
|
||||
|
||||
def test_edge_cases(self):
|
||||
"""测试边界情况"""
|
||||
# 0 不是质数
|
||||
result = self.checker.process(0)
|
||||
assert result["is_prime"] is False
|
||||
assert "reason" in result
|
||||
|
||||
# 1 不是质数
|
||||
result = self.checker.process(1)
|
||||
assert result["is_prime"] is False
|
||||
assert "reason" in result
|
||||
|
||||
# 2 是质数
|
||||
result = self.checker.process(2)
|
||||
assert result["is_prime"] is True
|
||||
|
||||
# 负数不是质数
|
||||
result = self.checker.process(-5)
|
||||
assert result["is_prime"] is False
|
||||
|
||||
def test_large_prime(self):
|
||||
"""测试大质数"""
|
||||
large_prime = 7919 # 第1000个质数
|
||||
result = self.checker.process(large_prime)
|
||||
assert result["is_prime"] is True
|
||||
|
||||
def test_invalid_input(self):
|
||||
"""测试无效输入"""
|
||||
with pytest.raises(ValueError):
|
||||
self.checker.process("not a number")
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
self.checker.process(3.14)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
self.checker.process(None)
|
||||
|
||||
def test_execute_method(self):
|
||||
"""测试 execute 方法(包含埋点)"""
|
||||
result = self.checker.execute(17)
|
||||
|
||||
assert result["success"] is True
|
||||
assert "result" in result
|
||||
assert "metadata" in result
|
||||
assert result["metadata"]["algorithm"] == "PrimeChecker"
|
||||
assert "elapsed_time" in result["metadata"]
|
||||
110
tests/test_api.py
Normal file
110
tests/test_api.py
Normal file
@@ -0,0 +1,110 @@
|
||||
"""API 集成测试"""
|
||||
|
||||
import pytest
|
||||
from fastapi import status
|
||||
|
||||
|
||||
class TestInvokeEndpoint:
|
||||
"""测试 /invoke 端点"""
|
||||
|
||||
def test_invoke_prime_number(self, client):
|
||||
"""测试质数判断"""
|
||||
response = client.post("/invoke", json={"number": 17})
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
|
||||
assert "request_id" in data
|
||||
assert data["status"] == "success"
|
||||
assert data["result"]["number"] == 17
|
||||
assert data["result"]["is_prime"] is True
|
||||
assert data["result"]["factors"] == []
|
||||
|
||||
def test_invoke_composite_number(self, client):
|
||||
"""测试合数判断"""
|
||||
response = client.post("/invoke", json={"number": 12})
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
|
||||
assert data["status"] == "success"
|
||||
assert data["result"]["number"] == 12
|
||||
assert data["result"]["is_prime"] is False
|
||||
assert len(data["result"]["factors"]) > 0
|
||||
|
||||
def test_invoke_edge_cases(self, client):
|
||||
"""测试边界情况"""
|
||||
# 测试 0
|
||||
response = client.post("/invoke", json={"number": 0})
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["result"]["is_prime"] is False
|
||||
|
||||
# 测试 1
|
||||
response = client.post("/invoke", json={"number": 1})
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["result"]["is_prime"] is False
|
||||
|
||||
# 测试 2
|
||||
response = client.post("/invoke", json={"number": 2})
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["result"]["is_prime"] is True
|
||||
|
||||
def test_invoke_invalid_input(self, client):
|
||||
"""测试无效输入"""
|
||||
# 缺少必需字段
|
||||
response = client.post("/invoke", json={})
|
||||
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
|
||||
|
||||
# 错误的数据类型
|
||||
response = client.post("/invoke", json={"number": "not a number"})
|
||||
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
|
||||
|
||||
# 浮点数
|
||||
response = client.post("/invoke", json={"number": 3.14})
|
||||
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
|
||||
|
||||
|
||||
class TestHealthEndpoints:
|
||||
"""测试健康检查端点"""
|
||||
|
||||
def test_healthz(self, client):
|
||||
"""测试存活检查"""
|
||||
response = client.get("/healthz")
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
|
||||
assert data["status"] == "healthy"
|
||||
assert "timestamp" in data
|
||||
|
||||
def test_readyz(self, client):
|
||||
"""测试就绪检查"""
|
||||
response = client.get("/readyz")
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
|
||||
assert data["status"] == "ready"
|
||||
assert "timestamp" in data
|
||||
assert "checks" in data
|
||||
|
||||
|
||||
class TestMetricsEndpoint:
|
||||
"""测试指标端点"""
|
||||
|
||||
def test_metrics(self, client):
|
||||
"""测试 Prometheus 指标"""
|
||||
response = client.get("/metrics")
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert "text/plain" in response.headers["content-type"]
|
||||
|
||||
|
||||
class TestJobsEndpoint:
|
||||
"""测试异步任务端点"""
|
||||
|
||||
def test_jobs_not_implemented(self, client):
|
||||
"""测试异步任务接口(未实现)"""
|
||||
response = client.post("/jobs", json={"number": 17})
|
||||
|
||||
assert response.status_code == status.HTTP_501_NOT_IMPLEMENTED
|
||||
Reference in New Issue
Block a user