Files
disknext/tests/integration/api/test_file_viewers.py
于小丘 eac0766e79 feat: migrate ORM base to sqlmodel-ext, add file viewers and WOPI integration
- Migrate SQLModel base classes, mixins, and database management to
  external sqlmodel-ext package; remove sqlmodels/base/, sqlmodels/mixin/,
  and sqlmodels/database.py
- Add file viewer/editor system with WOPI protocol support for
  collaborative editing (OnlyOffice, Collabora)
- Add enterprise edition license verification module (ee/)
- Add Dockerfile multi-stage build with Cython compilation support
- Add new dependencies: sqlmodel-ext, cryptography, whatthepatch

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 14:23:17 +08:00

306 lines
9.5 KiB
Python
Raw Permalink 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.
"""
文件查看器集成测试
测试查看器查询、用户默认设置、用户组过滤等端点。
"""
from uuid import UUID
import pytest
import pytest_asyncio
from httpx import AsyncClient
from sqlmodel.ext.asyncio.session import AsyncSession
from sqlmodels.file_app import (
FileApp,
FileAppExtension,
FileAppGroupLink,
FileAppType,
UserFileAppDefault,
)
from sqlmodels.user import User
# ==================== Fixtures ====================
@pytest_asyncio.fixture
async def setup_file_apps(
initialized_db: AsyncSession,
) -> dict[str, UUID]:
"""创建测试用文件查看器应用"""
# PDF 阅读器(不限制用户组)
pdf_app = FileApp(
name="PDF 阅读器",
app_key="pdfjs",
type=FileAppType.BUILTIN,
is_enabled=True,
is_restricted=False,
)
pdf_app = await pdf_app.save(initialized_db)
# Monaco 编辑器(不限制用户组)
monaco_app = FileApp(
name="代码编辑器",
app_key="monaco",
type=FileAppType.BUILTIN,
is_enabled=True,
is_restricted=False,
)
monaco_app = await monaco_app.save(initialized_db)
# Collabora限制用户组
collabora_app = FileApp(
name="Collabora",
app_key="collabora",
type=FileAppType.WOPI,
is_enabled=True,
is_restricted=True,
)
collabora_app = await collabora_app.save(initialized_db)
# 已禁用的应用
disabled_app = FileApp(
name="禁用的应用",
app_key="disabled_app",
type=FileAppType.BUILTIN,
is_enabled=False,
is_restricted=False,
)
disabled_app = await disabled_app.save(initialized_db)
# 创建扩展名
for ext in ["pdf"]:
await FileAppExtension(app_id=pdf_app.id, extension=ext, priority=0).save(initialized_db)
for ext in ["txt", "md", "json"]:
await FileAppExtension(app_id=monaco_app.id, extension=ext, priority=0).save(initialized_db)
for ext in ["docx", "xlsx", "pptx"]:
await FileAppExtension(app_id=collabora_app.id, extension=ext, priority=0).save(initialized_db)
for ext in ["pdf"]:
await FileAppExtension(app_id=disabled_app.id, extension=ext, priority=10).save(initialized_db)
return {
"pdf_app_id": pdf_app.id,
"monaco_app_id": monaco_app.id,
"collabora_app_id": collabora_app.id,
"disabled_app_id": disabled_app.id,
}
# ==================== GET /file/viewers ====================
class TestGetViewers:
"""查询可用查看器测试"""
@pytest.mark.asyncio
async def test_get_viewers_for_pdf(
self,
async_client: AsyncClient,
auth_headers: dict[str, str],
setup_file_apps: dict[str, UUID],
) -> None:
"""查询 PDF 查看器:返回已启用的,排除已禁用的"""
response = await async_client.get(
"/api/v1/file/viewers?ext=pdf",
headers=auth_headers,
)
assert response.status_code == 200
data = response.json()
assert "viewers" in data
viewer_keys = [v["app_key"] for v in data["viewers"]]
# pdfjs 应该在列表中
assert "pdfjs" in viewer_keys
# 禁用的应用不应出现
assert "disabled_app" not in viewer_keys
# 默认值应为 None
assert data["default_viewer_id"] is None
@pytest.mark.asyncio
async def test_get_viewers_normalizes_extension(
self,
async_client: AsyncClient,
auth_headers: dict[str, str],
setup_file_apps: dict[str, UUID],
) -> None:
"""扩展名规范化:.PDF → pdf"""
response = await async_client.get(
"/api/v1/file/viewers?ext=.PDF",
headers=auth_headers,
)
assert response.status_code == 200
data = response.json()
assert len(data["viewers"]) >= 1
@pytest.mark.asyncio
async def test_get_viewers_empty_for_unknown_ext(
self,
async_client: AsyncClient,
auth_headers: dict[str, str],
setup_file_apps: dict[str, UUID],
) -> None:
"""未知扩展名返回空列表"""
response = await async_client.get(
"/api/v1/file/viewers?ext=xyz_unknown",
headers=auth_headers,
)
assert response.status_code == 200
data = response.json()
assert data["viewers"] == []
@pytest.mark.asyncio
async def test_group_restriction_filters_app(
self,
async_client: AsyncClient,
initialized_db: AsyncSession,
auth_headers: dict[str, str],
setup_file_apps: dict[str, UUID],
) -> None:
"""用户组限制collabora 限制了用户组,用户不在白名单内则不可见"""
# collabora 是受限的,用户组不在白名单中
response = await async_client.get(
"/api/v1/file/viewers?ext=docx",
headers=auth_headers,
)
assert response.status_code == 200
data = response.json()
viewer_keys = [v["app_key"] for v in data["viewers"]]
assert "collabora" not in viewer_keys
# 将用户组加入白名单
test_user = await User.get(initialized_db, User.email == "testuser@test.local")
link = FileAppGroupLink(
app_id=setup_file_apps["collabora_app_id"],
group_id=test_user.group_id,
)
initialized_db.add(link)
await initialized_db.commit()
# 再次查询
response = await async_client.get(
"/api/v1/file/viewers?ext=docx",
headers=auth_headers,
)
assert response.status_code == 200
data = response.json()
viewer_keys = [v["app_key"] for v in data["viewers"]]
assert "collabora" in viewer_keys
@pytest.mark.asyncio
async def test_unauthorized_without_token(
self,
async_client: AsyncClient,
) -> None:
"""未认证请求返回 401"""
response = await async_client.get("/api/v1/file/viewers?ext=pdf")
assert response.status_code in (401, 403)
# ==================== User File Viewer Defaults ====================
class TestUserFileViewerDefaults:
"""用户默认查看器设置测试"""
@pytest.mark.asyncio
async def test_set_default_viewer(
self,
async_client: AsyncClient,
auth_headers: dict[str, str],
setup_file_apps: dict[str, UUID],
) -> None:
"""设置默认查看器"""
response = await async_client.put(
"/api/v1/user/settings/file-viewers/default",
headers=auth_headers,
json={
"extension": "pdf",
"app_id": str(setup_file_apps["pdf_app_id"]),
},
)
assert response.status_code == 200
data = response.json()
assert data["extension"] == "pdf"
assert data["app"]["app_key"] == "pdfjs"
@pytest.mark.asyncio
async def test_list_default_viewers(
self,
async_client: AsyncClient,
initialized_db: AsyncSession,
auth_headers: dict[str, str],
setup_file_apps: dict[str, UUID],
) -> None:
"""列出默认查看器"""
# 先创建一个默认
test_user = await User.get(initialized_db, User.email == "testuser@test.local")
await UserFileAppDefault(
user_id=test_user.id,
extension="pdf",
app_id=setup_file_apps["pdf_app_id"],
).save(initialized_db)
response = await async_client.get(
"/api/v1/user/settings/file-viewers/defaults",
headers=auth_headers,
)
assert response.status_code == 200
data = response.json()
assert isinstance(data, list)
assert len(data) >= 1
@pytest.mark.asyncio
async def test_delete_default_viewer(
self,
async_client: AsyncClient,
initialized_db: AsyncSession,
auth_headers: dict[str, str],
setup_file_apps: dict[str, UUID],
) -> None:
"""撤销默认查看器"""
# 创建一个默认
test_user = await User.get(initialized_db, User.email == "testuser@test.local")
default = await UserFileAppDefault(
user_id=test_user.id,
extension="txt",
app_id=setup_file_apps["monaco_app_id"],
).save(initialized_db)
response = await async_client.delete(
f"/api/v1/user/settings/file-viewers/default/{default.id}",
headers=auth_headers,
)
assert response.status_code == 204
# 验证已删除
found = await UserFileAppDefault.get(
initialized_db, UserFileAppDefault.id == default.id
)
assert found is None
@pytest.mark.asyncio
async def test_get_viewers_includes_default(
self,
async_client: AsyncClient,
initialized_db: AsyncSession,
auth_headers: dict[str, str],
setup_file_apps: dict[str, UUID],
) -> None:
"""查看器查询应包含用户默认选择"""
# 设置默认
test_user = await User.get(initialized_db, User.email == "testuser@test.local")
await UserFileAppDefault(
user_id=test_user.id,
extension="pdf",
app_id=setup_file_apps["pdf_app_id"],
).save(initialized_db)
response = await async_client.get(
"/api/v1/file/viewers?ext=pdf",
headers=auth_headers,
)
assert response.status_code == 200
data = response.json()
assert data["default_viewer_id"] == str(setup_file_apps["pdf_app_id"])