AgentEvalTool/tests/integration/test_files_api.py

201 lines
6.2 KiB
Python

"""Integration tests for the file management API."""
from pathlib import Path
import pytest
from agenteval.config import get_settings
from agenteval.web.app import app
from agenteval.web.deps import get_db
from fastapi.testclient import TestClient
from sqlmodel import Session, SQLModel, create_engine
@pytest.fixture()
def files_client(tmp_path: Path, monkeypatch):
from agenteval.storage.db import ( # noqa: F401
EvalResultDB,
EvalRunDB,
EvalTargetDB,
FileCategoryDB,
FileRecordDB,
ScenarioDB,
TurnDB,
)
from agenteval.web.routers import files as files_module
engine = create_engine(
f"sqlite:///{tmp_path / 'files_api.db'}",
connect_args={"check_same_thread": False},
)
SQLModel.metadata.create_all(engine)
session = Session(engine)
files_dir = tmp_path / "files"
files_dir.mkdir()
monkeypatch.setattr(files_module, "FILES_DIR", files_dir)
settings = get_settings()
monkeypatch.setattr(settings, "allowed_extensions", "txt,json")
monkeypatch.setattr(settings, "max_upload_size_mb", 1)
def override_get_db():
yield session
app.dependency_overrides[get_db] = override_get_db
with TestClient(app) as client:
yield client, session, files_dir
app.dependency_overrides.clear()
session.close()
engine.dispose()
def _create_category(client: TestClient, name: str, parent_id: str | None = None) -> dict:
response = client.post(
"/api/files/categories",
json={"name": name, "parent_id": parent_id},
)
assert response.status_code == 200
return response.json()
def test_upload_config_uses_server_settings(files_client):
client, _, _ = files_client
response = client.get("/api/files/config")
assert response.status_code == 200
assert response.json() == {
"allowed_extensions": ["json", "txt"],
"max_upload_size_mb": 1,
}
def test_category_crud_returns_domain_fields_and_deep_tree(files_client):
client, _, _ = files_client
root = _create_category(client, "根分类")
child = _create_category(client, "二级", root["id"])
leaf = _create_category(client, "三级", child["id"])
tree = client.get("/api/files/categories").json()
assert tree[0]["id"] == root["id"]
assert tree[0]["name"] == "根分类"
assert tree[0]["children"][0]["id"] == child["id"]
assert tree[0]["children"][0]["children"][0]["id"] == leaf["id"]
updated = client.put(
f"/api/files/categories/{leaf['id']}",
json={"name": "三级分类"},
)
assert updated.status_code == 200
assert updated.json()["name"] == "三级分类"
def test_create_category_rejects_missing_parent(files_client):
client, _, _ = files_client
response = client.post(
"/api/files/categories",
json={"name": "孤立分类", "parent_id": "missing"},
)
assert response.status_code == 400
assert response.json()["detail"] == "父分类不存在"
def test_list_files_includes_all_descendant_categories(files_client):
client, _, _ = files_client
root = _create_category(client, "根分类")
child = _create_category(client, "二级", root["id"])
leaf = _create_category(client, "三级", child["id"])
upload = client.post(
"/api/files/upload",
data={"category_id": leaf["id"]},
files={"file": ("deep.txt", b"deep content", "text/plain")},
)
assert upload.status_code == 200
response = client.get("/api/files", params={"category_id": root["id"]})
assert response.status_code == 200
assert [item["original_name"] for item in response.json()] == ["deep.txt"]
def test_upload_download_and_delete_file(files_client):
client, _, files_dir = files_client
uploaded = client.post(
"/api/files/upload",
files={"file": ("notes.txt", b"hello", "text/plain")},
)
assert uploaded.status_code == 200
record = uploaded.json()
assert record["original_name"] == "notes.txt"
assert record["file_size"] == 5
assert record["storage_directory"] == "data/files"
assert client.get("/api/files").json()[0]["storage_directory"] == "data/files"
assert len(list(files_dir.iterdir())) == 1
downloaded = client.get(f"/api/files/{record['id']}/download")
assert downloaded.status_code == 200
assert downloaded.content == b"hello"
deleted = client.delete(f"/api/files/{record['id']}")
assert deleted.status_code == 200
assert list(files_dir.iterdir()) == []
assert client.get("/api/files").json() == []
def test_upload_rejects_invalid_category_and_extension(files_client):
client, _, files_dir = files_client
invalid_category = client.post(
"/api/files/upload",
data={"category_id": "missing"},
files={"file": ("notes.txt", b"hello", "text/plain")},
)
invalid_extension = client.post(
"/api/files/upload",
files={"file": ("notes.exe", b"hello", "application/octet-stream")},
)
assert invalid_category.status_code == 400
assert invalid_category.json()["detail"] == "分类不存在"
assert invalid_extension.status_code == 400
assert list(files_dir.iterdir()) == []
def test_upload_rejects_oversized_file_without_disk_artifact(files_client):
client, _, files_dir = files_client
response = client.post(
"/api/files/upload",
files={"file": ("large.txt", b"x" * (1024 * 1024 + 1), "text/plain")},
)
assert response.status_code == 413
assert list(files_dir.iterdir()) == []
assert client.get("/api/files").json() == []
def test_delete_category_removes_descendant_files(files_client):
client, _, files_dir = files_client
root = _create_category(client, "根分类")
child = _create_category(client, "子分类", root["id"])
uploaded = client.post(
"/api/files/upload",
data={"category_id": child["id"]},
files={"file": ("child.txt", b"content", "text/plain")},
)
assert uploaded.status_code == 200
assert len(list(files_dir.iterdir())) == 1
response = client.delete(f"/api/files/categories/{root['id']}")
assert response.status_code == 200
assert client.get("/api/files/categories").json() == []
assert client.get("/api/files").json() == []
assert list(files_dir.iterdir()) == []