fix(arch-043,arch-052): deterministic system tenant lookup; async-safe file metadata
Check Cross-Plugin Imports / check (push) Has been cancelled
Check Cross-Plugin Imports / check (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
# Regression tests for Block A fixes: ARCH-043 (system tenant) and ARCH-052 (storage metadata).
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
# --- ARCH-043: get_system_tenant resolves by configured slug ---
|
||||
|
||||
|
||||
class TestGetSystemTenant:
|
||||
@pytest.mark.asyncio
|
||||
async def test_resolves_by_slug_not_first_row(self, db_session):
|
||||
from sqlalchemy import select
|
||||
|
||||
from app.config import get_settings
|
||||
from app.core.db import get_system_tenant
|
||||
from app.models.tenant import Tenant
|
||||
|
||||
settings = get_settings()
|
||||
slug = settings.system_tenant_slug
|
||||
|
||||
# Insert a decoy first and the real system tenant second,
|
||||
# so a limit(1)-style query would pick the wrong row.
|
||||
decoy = Tenant(name='Decoy Org', slug=f'decoy-{uuid.uuid4().hex[:8]}')
|
||||
db_session.add(decoy)
|
||||
system = Tenant(name='Default Org', slug=slug)
|
||||
db_session.add(system)
|
||||
await db_session.commit()
|
||||
|
||||
try:
|
||||
tenant = await get_system_tenant(db_session)
|
||||
assert tenant is not None
|
||||
assert tenant.slug == slug
|
||||
# sanity: decoy really is the first row
|
||||
first = (await db_session.execute(select(Tenant).limit(1))).scalar_one()
|
||||
assert first.id != tenant.id
|
||||
finally:
|
||||
await db_session.delete(decoy)
|
||||
await db_session.delete(system)
|
||||
await db_session.commit()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_none_when_missing(self, db_session):
|
||||
from sqlalchemy import delete
|
||||
|
||||
from app.core.db import get_system_tenant
|
||||
from app.models.tenant import Tenant
|
||||
|
||||
await db_session.execute(delete(Tenant))
|
||||
await db_session.commit()
|
||||
|
||||
tenant = await get_system_tenant(db_session)
|
||||
assert tenant is None
|
||||
|
||||
|
||||
# --- ARCH-052: async metadata without nested event loops ---
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def tmp_storage(tmp_path, monkeypatch):
|
||||
from app.core.storage import LocalStorage, reset_storage_backend
|
||||
|
||||
monkeypatch.setenv('STORAGE_PATH', str(tmp_path))
|
||||
monkeypatch.setenv('STORAGE_BACKEND', 'local')
|
||||
reset_storage_backend()
|
||||
backend = LocalStorage(base_path=str(tmp_path))
|
||||
yield backend
|
||||
reset_storage_backend()
|
||||
|
||||
|
||||
class TestGetFileMetadataAsync:
|
||||
def _use_local(self, tmp_storage, monkeypatch):
|
||||
monkeypatch.setenv('STORAGE_PATH', str(tmp_storage.base_path))
|
||||
monkeypatch.setenv('STORAGE_BACKEND', 'local')
|
||||
from app.core.storage import reset_storage_backend
|
||||
|
||||
reset_storage_backend()
|
||||
import app.core.storage as storage_mod
|
||||
|
||||
storage_mod._storage_backend = tmp_storage
|
||||
|
||||
def teardown_method(self):
|
||||
from app.core.storage import reset_storage_backend
|
||||
|
||||
reset_storage_backend()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_awaitable_inside_running_loop(self, tmp_storage, monkeypatch):
|
||||
self._use_local(tmp_storage, monkeypatch)
|
||||
|
||||
from app.core.storage import get_file_metadata_async
|
||||
|
||||
await tmp_storage.save('meta/async.txt', b'async metadata')
|
||||
|
||||
meta = await get_file_metadata_async('meta/async.txt')
|
||||
assert meta['exists'] is True
|
||||
assert meta['size'] == len(b'async metadata')
|
||||
assert meta['modified'] is not None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_non_existing_inside_running_loop(self, tmp_storage, monkeypatch):
|
||||
self._use_local(tmp_storage, monkeypatch)
|
||||
|
||||
from app.core.storage import get_file_metadata_async
|
||||
|
||||
meta = await get_file_metadata_async('nonexistent/async.txt')
|
||||
assert meta['exists'] is False
|
||||
assert meta['size'] is None
|
||||
assert meta['modified'] is None
|
||||
|
||||
def test_sync_wrapper_still_works(self, tmp_storage, monkeypatch):
|
||||
self._use_local(tmp_storage, monkeypatch)
|
||||
|
||||
from app.core.storage import get_file_metadata
|
||||
|
||||
asyncio.run(tmp_storage.save('meta/sync.txt', b'sync metadata'))
|
||||
meta = get_file_metadata('meta/sync.txt')
|
||||
assert meta['exists'] is True
|
||||
assert meta['size'] == len(b'sync metadata')
|
||||
|
||||
def test_sync_wrapper_non_existing(self, tmp_storage, monkeypatch):
|
||||
self._use_local(tmp_storage, monkeypatch)
|
||||
|
||||
from app.core.storage import get_file_metadata
|
||||
|
||||
meta = get_file_metadata('nonexistent/sync.txt')
|
||||
assert meta == {'size': None, 'modified': None, 'exists': False}
|
||||
Reference in New Issue
Block a user