"""任务接口测试""" 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 oss_url = "https://files.example.com/test/iOS/1_2_0_0_0_main.html" 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=oss_url, 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 delete.call_args.args[2] == oss_url 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()