import os from fastapi.testclient import TestClient os.environ.setdefault("DODO_DATABASE_URL", "sqlite+aiosqlite:////tmp/dodo-health-test.db") os.environ.setdefault("DODO_AUTO_CREATE_SCHEMA", "true") from backend.main import app def test_health_live(): with TestClient(app) as client: response = client.get("/health/live") assert response.status_code == 200 assert response.json() == {"status": "ok"} def test_setup_status_starts_uninitialized(client): response = client.get("/api/v1/setup/status") assert response.status_code == 200 assert response.json() == {"initialized": False} def test_initialize_login_and_create_task(client): initialized = client.post( "/api/v1/setup/initialize", json={"username": "owner", "password": "correct horse battery staple"}, ) assert initialized.status_code == 201 assert initialized.json()["username"] == "owner" assert "dodo_session" in initialized.cookies folder = client.post("/api/v1/folders", json={"name": "工作"}) assert folder.status_code == 201 task_list = client.post( "/api/v1/lists", json={"name": "本周", "folder_id": folder.json()["id"]} ) assert task_list.status_code == 201 task = client.post( "/api/v1/tasks", json={"title": "完成 dodo 第一阶段", "list_id": task_list.json()["id"], "priority": 3}, ) assert task.status_code == 201 assert task.json()["title"] == "完成 dodo 第一阶段" tasks = client.get("/api/v1/tasks") assert tasks.status_code == 200 assert len(tasks.json()["items"]) == 1 def test_initialize_is_closed_after_first_user(client): payload = {"username": "owner", "password": "correct horse battery staple"} assert client.post("/api/v1/setup/initialize", json=payload).status_code == 201 response = client.post("/api/v1/setup/initialize", json=payload) assert response.status_code == 409 def test_unauthenticated_task_access_is_rejected(client): response = client.get("/api/v1/tasks") assert response.status_code == 401 def test_task_can_be_updated_completed_and_soft_deleted(client): client.post( "/api/v1/setup/initialize", json={"username": "owner", "password": "correct horse battery staple"}, ) inbox = client.get("/api/v1/lists").json()[0] task = client.post( "/api/v1/tasks", json={"title": "旧标题", "list_id": inbox["id"]} ).json() updated = client.patch( f"/api/v1/tasks/{task['id']}", json={"title": "新标题", "completed": True, "version": task["version"]}, ) assert updated.status_code == 200 assert updated.json()["title"] == "新标题" assert updated.json()["completed"] is True assert updated.json()["version"] == 2 conflict = client.patch( f"/api/v1/tasks/{task['id']}", json={"title": "冲突标题", "version": task["version"]}, ) assert conflict.status_code == 409 deleted = client.delete(f"/api/v1/tasks/{task['id']}") assert deleted.status_code == 204 assert client.get("/api/v1/tasks").json()["items"] == [] def test_task_update_rejects_null_title(client): client.post( "/api/v1/setup/initialize", json={"username": "owner", "password": "correct horse battery staple"}, ) inbox = client.get("/api/v1/lists").json()[0] task = client.post("/api/v1/tasks", json={"title": "任务", "list_id": inbox["id"]}).json() response = client.patch( f"/api/v1/tasks/{task['id']}", json={"title": None, "version": task["version"]} ) assert response.status_code == 422 def test_subtask_parent_must_belong_to_same_user_and_list(client): client.post( "/api/v1/setup/initialize", json={"username": "owner", "password": "correct horse battery staple"}, ) inbox = client.get("/api/v1/lists").json()[0] other = client.post("/api/v1/lists", json={"name": "其他"}).json() parent = client.post("/api/v1/tasks", json={"title": "父任务", "list_id": inbox["id"]}).json() response = client.post( "/api/v1/tasks", json={"title": "子任务", "list_id": other["id"], "parent_id": parent["id"]}, ) assert response.status_code == 400 def test_logout_revokes_current_session(client): client.post( "/api/v1/setup/initialize", json={"username": "owner", "password": "correct horse battery staple"}, ) assert client.get("/api/v1/me").status_code == 200 assert client.post("/api/v1/auth/logout").status_code == 204 assert client.get("/api/v1/me").status_code == 401