aps-agent/tests/golden/test_project_file_upload.py

120 lines
4.1 KiB
Python
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.

# ============================================================
# 项目文件上传/下载黄金测试(Web 端持久化到数据目录)
# 口径:字节落 project_upload_dir(tenants/<租户>/projects/<项目>/uploads),
# 元数据走 ProjectFileRecord;viewer 403;删除记录联动清理字节。
# ============================================================
from __future__ import annotations
import io
import pytest
from fastapi.testclient import TestClient
from tests.auth_provider import install_test_auth
@pytest.fixture()
def secure_app(tmp_path, monkeypatch):
monkeypatch.setenv("APS_DB_PATH", str(tmp_path / "tenant.db"))
monkeypatch.setenv("APS_WORLD_PATH", str(tmp_path / "world.json"))
from server.db.database import reset_engine
from server.state import store as world_store
install_test_auth(monkeypatch, "tenant-upload-00000000000000000001")
world_store._stores.clear()
reset_engine()
from server.gateway.app import create_app
app = create_app()
yield app
reset_engine()
world_store._stores.clear()
def _login(client: TestClient, username: str = "planner") -> None:
resp = client.post("/api/auth/login", json={
"method": "password", "username": username, "password": "test",
})
assert resp.status_code == 200
def _create_project(client: TestClient, name: str = "上传测试项目") -> str:
resp = client.post("/api/projects", json={"name": name})
assert resp.status_code == 200
return resp.json()["project"]["id"]
def test_upload_download_roundtrip(secure_app):
client = TestClient(secure_app)
_login(client)
pid = _create_project(client)
payload = "orderNo,qty\nFO-1,100\n".encode("utf-8")
resp = client.post(
f"/api/projects/{pid}/files/upload",
files=[("files", ("orders.csv", io.BytesIO(payload), "text/csv"))],
)
assert resp.status_code == 200, resp.text
body = resp.json()
assert body["files"][0]["name"] == "orders.csv"
assert body["files"][0]["kind"] == "import"
assert body["files"][0]["size"] == len(payload)
file_id = body["files"][0]["id"]
# 字节落数据目录(conftest 隔离的 APS_HOME/data 下)
from server.aps_home import project_upload_dir
folder = project_upload_dir(pid, "tenant-upload-00000000000000000001")
blobs = list(folder.glob(f"{file_id}__*"))
assert len(blobs) == 1
assert blobs[0].read_bytes() == payload
# 记录出现在工作区快照
snap = client.get("/api/workspace").json()
assert any(f["id"] == file_id for f in snap["files"])
# 下载回源字节
dl = client.get(f"/api/project-files/{file_id}/download")
assert dl.status_code == 200
assert dl.content == payload
# 删除记录 → 字节联动清理
assert client.delete(f"/api/project-files/{file_id}").status_code == 200
assert not list(folder.glob(f"{file_id}__*"))
assert client.get(f"/api/project-files/{file_id}/download").status_code == 404
def test_upload_requires_login(secure_app):
client = TestClient(secure_app)
resp = client.post(
"/api/projects/proj_x/files/upload",
files=[("files", ("a.csv", io.BytesIO(b"x"), "text/csv"))],
)
assert resp.status_code == 401
def test_upload_viewer_forbidden(secure_app):
owner = TestClient(secure_app)
_login(owner, "planner")
pid = _create_project(owner)
# owner 邀请 viewer 为只读成员
resp = owner.post(f"/api/projects/{pid}/members", json={"userId": 1003, "role": "viewer"})
assert resp.status_code == 200, resp.text
viewer = TestClient(secure_app)
_login(viewer, "viewer")
resp = viewer.post(
f"/api/projects/{pid}/files/upload",
files=[("files", ("a.csv", io.BytesIO(b"x"), "text/csv"))],
)
assert resp.status_code == 403
def test_upload_rejects_empty_and_cross_tenant_blob(secure_app):
client = TestClient(secure_app)
_login(client)
pid = _create_project(client)
resp = client.post(
f"/api/projects/{pid}/files/upload",
files=[("files", ("empty.csv", io.BytesIO(b""), "text/csv"))],
)
assert resp.status_code == 400