T04: DMS plugin backend — folders + files + preview + OnlyOffice + shares + search + bulk — 106 tests, 97.90% coverage
This commit is contained in:
+67
-1
@@ -7,6 +7,8 @@ Auth helpers talk to the HTTP API (integration tests).
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import shutil
|
||||
from collections.abc import AsyncGenerator
|
||||
from typing import Any
|
||||
|
||||
@@ -24,6 +26,7 @@ from sqlalchemy.ext.asyncio import (
|
||||
|
||||
from app.core.auth import hash_password
|
||||
from app.core.db import Base, close_engine, reset_engine_for_testing
|
||||
from app.core.service_container import get_container # noqa: F401
|
||||
from app.main import create_app
|
||||
from app.models.ai_conversation import AIConversation, AIMessage # noqa: F401
|
||||
from app.models.company import Company
|
||||
@@ -33,9 +36,15 @@ from app.models.role import Role
|
||||
from app.models.tenant import Tenant
|
||||
from app.models.user import User, UserTenant
|
||||
from app.models.workflow import Workflow, WorkflowInstance, WorkflowStepHistory # noqa: F401
|
||||
from app.plugins.builtins.dms import DmsPlugin # noqa: F401
|
||||
from app.plugins.builtins.dms.models import File as DmsFile # noqa: F401
|
||||
from app.plugins.builtins.dms.models import Folder # noqa: F401
|
||||
from app.plugins.builtins.entity_links.models import EntityLink # noqa: F401
|
||||
from app.plugins.builtins.permissions import PermissionsPlugin # noqa: F401
|
||||
from app.plugins.builtins.permissions.models import Permission, ShareLink # noqa: F401
|
||||
from app.plugins.builtins.tags.models import Tag, TagAssignment # noqa: F401
|
||||
from app.plugins.registry import reset_registry_for_testing # noqa: F401
|
||||
from app.services.plugin_service import reset_plugin_service_for_testing # noqa: F401
|
||||
|
||||
# Import plugin models so Base.metadata.create_all includes their tables
|
||||
|
||||
@@ -94,7 +103,7 @@ def clean_tables(db_setup):
|
||||
# TRUNCATE all tables with CASCADE — fast and reliable isolation
|
||||
conn.execute(
|
||||
text(
|
||||
"TRUNCATE TABLE entity_links, share_links, permissions, tag_assignments, tags, workflow_step_history, workflow_instances, workflows, ai_messages, ai_conversations, plugin_migrations, plugins, company_contacts, contacts, api_tokens, password_reset_tokens, notifications, deletion_log, audit_log, sessions, roles, companies, user_tenants, users, tenants CASCADE;"
|
||||
"TRUNCATE TABLE files, folders, entity_links, share_links, permissions, tag_assignments, tags, workflow_step_history, workflow_instances, workflows, ai_messages, ai_conversations, plugin_migrations, plugins, company_contacts, contacts, api_tokens, password_reset_tokens, notifications, deletion_log, audit_log, sessions, roles, companies, user_tenants, users, tenants CASCADE;"
|
||||
)
|
||||
)
|
||||
conn.commit()
|
||||
@@ -283,3 +292,60 @@ async def get_auth_client(
|
||||
"""Return a client that's logged in."""
|
||||
await login_client(client, email, password)
|
||||
return client
|
||||
|
||||
|
||||
DMS_TEST_STORAGE = "/tmp/dms_test"
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def dms_app(engine: AsyncEngine, redis_client):
|
||||
"""FastAPI app with DMS + Permissions plugins registered."""
|
||||
os.environ["DMS_STORAGE_BASE"] = DMS_TEST_STORAGE
|
||||
reset_engine_for_testing(engine)
|
||||
app = create_app()
|
||||
|
||||
registry = reset_registry_for_testing()
|
||||
registry.initialize(engine, app)
|
||||
|
||||
container = get_container()
|
||||
await container.initialize()
|
||||
|
||||
registry.register_plugin(PermissionsPlugin())
|
||||
registry.register_plugin(DmsPlugin())
|
||||
reset_plugin_service_for_testing(registry)
|
||||
|
||||
yield app
|
||||
await close_engine()
|
||||
# Cleanup test storage
|
||||
if os.path.exists(DMS_TEST_STORAGE):
|
||||
shutil.rmtree(DMS_TEST_STORAGE, ignore_errors=True)
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def dms_client(dms_app) -> AsyncClient:
|
||||
transport = ASGITransport(app=dms_app)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as c:
|
||||
yield c
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def authed_client(
|
||||
dms_client: AsyncClient, db_session: AsyncSession
|
||||
) -> tuple[AsyncClient, dict]:
|
||||
"""Authenticated admin client with seeded data and both plugins activated."""
|
||||
seed = await seed_tenant_and_users(db_session)
|
||||
await login_client(dms_client, "admin@tenanta.com")
|
||||
|
||||
# Install + activate permissions plugin first (DMS depends on it)
|
||||
resp = await dms_client.post("/api/v1/plugins/permissions/install", headers=ORIGIN_HEADER)
|
||||
assert resp.status_code == 200, f"Permissions install failed: {resp.text}"
|
||||
resp = await dms_client.post("/api/v1/plugins/permissions/activate", headers=ORIGIN_HEADER)
|
||||
assert resp.status_code == 200, f"Permissions activate failed: {resp.text}"
|
||||
|
||||
# Install + activate DMS plugin
|
||||
resp = await dms_client.post("/api/v1/plugins/dms/install", headers=ORIGIN_HEADER)
|
||||
assert resp.status_code == 200, f"DMS install failed: {resp.text}"
|
||||
resp = await dms_client.post("/api/v1/plugins/dms/activate", headers=ORIGIN_HEADER)
|
||||
assert resp.status_code == 200, f"DMS activate failed: {resp.text}"
|
||||
|
||||
return dms_client, seed
|
||||
|
||||
Reference in New Issue
Block a user