iOSBuildServer/tests/test_api_tasks.py
2026-07-20 16:55:17 +09:00

215 lines
6.8 KiB
Python

"""任务接口测试"""
import json
from unittest.mock import patch, AsyncMock
def test_list_tasks_empty(client, tmp_config):
resp = client.get("/api/tasks")
assert resp.status_code == 200
assert resp.json() == []
@patch("backend.services.build_queue.build_queue")
def test_create_task(mock_queue, client, tmp_config):
mock_queue.submit = AsyncMock()
resp = client.post("/api/tasks", json={
"app_id": "1",
"build_type": "Ad_Hoc",
"scheme_id": "1",
"obfuscation": True,
"branch": "main",
})
assert resp.status_code == 200
data = resp.json()
assert data["status"] == "pending"
assert data["app_name"] == "测试App"
assert data["scheme_name"] == "readoor31"
assert data["branch"] == "main"
assert data["build_type"] == "Ad_Hoc"
@patch("backend.services.build_queue.build_queue")
def test_create_task_default_branch(mock_queue, client, tmp_config):
mock_queue.submit = AsyncMock()
resp = client.post("/api/tasks", json={
"app_id": "1",
"build_type": "Ad_Hoc",
"scheme_id": "1",
})
assert resp.status_code == 200
assert resp.json()["branch"] == "main"
@patch("backend.services.build_queue.build_queue")
def test_create_task_invalid_app(mock_queue, client, tmp_config):
mock_queue.submit = AsyncMock()
resp = client.post("/api/tasks", json={
"app_id": "999",
"build_type": "Ad_Hoc",
"scheme_id": "1",
})
assert resp.status_code == 400
@patch("backend.services.build_queue.build_queue")
def test_create_task_invalid_scheme(mock_queue, client, tmp_config):
mock_queue.submit = AsyncMock()
resp = client.post("/api/tasks", json={
"app_id": "1",
"build_type": "Ad_Hoc",
"scheme_id": "999",
})
assert resp.status_code == 400
@patch("backend.services.build_queue.build_queue")
def test_create_task_rejects_unconfigured_certificate_type(mock_queue, client, tmp_config):
config = json.loads(tmp_config.read_text())
config["apps"]["1"]["certificates"] = {
"Ad_Hoc": {"name": "com.test.app"},
}
tmp_config.write_text(json.dumps(config))
resp = client.post("/api/tasks", json={
"app_id": "1", "build_type": "App_Store", "scheme_id": "1", "branch": "main",
})
assert resp.status_code == 400
assert "未配置 App_Store 证书" in resp.json()["detail"]
@patch("backend.services.build_queue.build_queue")
def test_create_task_rejects_disallowed_special_app_scheme(mock_queue, client, tmp_config):
config = json.loads(tmp_config.read_text())
config["apps"]["1"].update({
"name": "英汉大词典",
"certificates": {"Ad_Hoc": {"name": "com.dictionary.app"}},
})
config["schemes"] = {
"1": {"name": "readoor31"},
"2": {"name": "readoorDict"},
}
tmp_config.write_text(json.dumps(config))
resp = client.post("/api/tasks", json={
"app_id": "1", "build_type": "Ad_Hoc", "scheme_id": "1", "branch": "main",
})
assert resp.status_code == 400
assert "readoorDict" in resp.json()["detail"]
@patch("backend.services.build_queue.build_queue")
def test_create_task_rejects_non_default_scheme_for_regular_app(mock_queue, client, tmp_config):
config = json.loads(tmp_config.read_text())
config["schemes"] = {
"1": {"name": "readoor31"},
"2": {"name": "readoorDict"},
}
tmp_config.write_text(json.dumps(config))
resp = client.post("/api/tasks", json={
"app_id": "1", "build_type": "Ad_Hoc", "scheme_id": "2", "branch": "main",
})
assert resp.status_code == 400
assert "readoor31OtherPay" in resp.json()["detail"]
@patch("backend.services.build_queue.build_queue")
def test_list_tasks_after_create(mock_queue, client, tmp_config):
mock_queue.submit = AsyncMock()
client.post("/api/tasks", json={
"app_id": "1",
"build_type": "Ad_Hoc",
"scheme_id": "1",
})
tasks = client.get("/api/tasks").json()
assert len(tasks) == 1
assert tasks[0]["app_name"] == "测试App"
@patch("backend.services.build_queue.build_queue")
def test_get_task_detail(mock_queue, client, tmp_config):
mock_queue.submit = AsyncMock()
create_resp = client.post("/api/tasks", json={
"app_id": "1",
"build_type": "Ad_Hoc",
"scheme_id": "1",
})
task_id = create_resp.json()["id"]
resp = client.get(f"/api/tasks/{task_id}")
assert resp.status_code == 200
assert resp.json()["id"] == task_id
def test_get_nonexistent_task(client, tmp_config):
resp = client.get("/api/tasks/nonexistent")
assert resp.status_code == 404
@patch("backend.services.build_queue.build_queue")
def test_cancel_task(mock_queue, client, tmp_config):
mock_queue.submit = AsyncMock()
mock_queue.cancel = lambda x: None
create_resp = client.post("/api/tasks", json={
"app_id": "1",
"build_type": "Ad_Hoc",
"scheme_id": "1",
})
task_id = create_resp.json()["id"]
resp = client.delete(f"/api/tasks/{task_id}")
assert resp.status_code == 200
# 验证状态已更新
task = client.get(f"/api/tasks/{task_id}").json()
assert task["status"] == "cancelled"
def test_delete_completed_task_removes_remote_artifacts(client, tmp_config):
from backend.database import SessionLocal
from backend.models import Task
task = Task(
id="completed-task", app_id="1", app_name="测试App", build_type="Ad_Hoc",
scheme_id="1", scheme_name="readoor31", branch="main", status="completed",
oss_url="https://files.example.com/test/iOS/1_2_0_0_0_main.html",
config_json=json.dumps({"APPID": "1", "VERSION": "2.0.0.0", "SOURCE_BRANCH": "main", "BUILD_TYPE": "Ad_Hoc", "OSS_FLODER": "test"}),
)
db = SessionLocal()
db.add(task)
db.commit()
db.close()
with patch("backend.services.distribution.delete_published_artifacts") as delete:
resp = client.delete("/api/tasks/completed-task/delete")
assert resp.status_code == 200
delete.assert_called_once()
assert client.get("/api/tasks/completed-task").status_code == 404
def test_delete_task_keeps_shared_remote_artifact(client, tmp_config):
from backend.database import SessionLocal
from backend.models import Task
db = SessionLocal()
for task_id in ("old-task", "new-task"):
db.add(Task(
id=task_id, app_id="1", app_name="测试App", build_type="Ad_Hoc",
scheme_id="1", scheme_name="readoor31", branch="main", status="completed",
oss_url="https://files.example.com/test/iOS/1_2_0_0_0_main.html",
))
db.commit()
db.close()
with patch("backend.services.distribution.delete_published_artifacts") as delete:
resp = client.delete("/api/tasks/old-task/delete")
assert resp.status_code == 200
assert "仍被其他记录引用" in resp.json()["message"]
delete.assert_not_called()