mirror of
https://github.com/insistence/RuoYi-Vue3-FastAPI.git
synced 2026-09-21 04:46:28 +00:00
285 lines
8.9 KiB
Python
285 lines
8.9 KiB
Python
import asyncio
|
|
from datetime import datetime, timedelta, timezone
|
|
from types import SimpleNamespace
|
|
from unittest.mock import AsyncMock, patch
|
|
|
|
import pytest
|
|
from sqlalchemy import true
|
|
|
|
from exceptions.exception import ServiceException
|
|
from module_admin.dao.file_business_dao import FileReferenceDao
|
|
from module_admin.dao.file_info_dao import FileInfoDao
|
|
from module_admin.entity.do.file_do import SysFileReference
|
|
from module_admin.entity.vo.file_vo import FileRetentionPolicyModel
|
|
from module_admin.service.file_business_service import FileReferenceService, FileRetentionPolicyService
|
|
|
|
FILE_ID = '11111111-1111-4111-8111-111111111111'
|
|
|
|
|
|
def make_query_db() -> SimpleNamespace:
|
|
return SimpleNamespace(commit=AsyncMock(), rollback=AsyncMock())
|
|
|
|
|
|
def test_get_file_reference_list_includes_legacy_reference() -> None:
|
|
file_info = {
|
|
'file_id': FILE_ID,
|
|
'business_type': 'notice',
|
|
'business_id': '10',
|
|
}
|
|
with (
|
|
patch.object(
|
|
FileInfoDao,
|
|
'get_file_management_detail_by_id',
|
|
new=AsyncMock(return_value=file_info),
|
|
),
|
|
patch.object(FileReferenceDao, 'get_file_reference_list', new=AsyncMock(return_value=[])),
|
|
):
|
|
result = asyncio.run(
|
|
FileReferenceService.get_file_reference_list_services(
|
|
make_query_db(),
|
|
FILE_ID,
|
|
true(),
|
|
)
|
|
)
|
|
|
|
assert len(result) == 1
|
|
assert result[0].business_type == 'notice'
|
|
assert result[0].business_id == '10'
|
|
assert result[0].legacy is True
|
|
|
|
|
|
def test_get_file_reference_list_converts_orm_reference() -> None:
|
|
create_time = datetime(2026, 7, 26, 10, 0, 0, tzinfo=timezone.utc)
|
|
file_info = {
|
|
'file_id': FILE_ID,
|
|
'business_type': None,
|
|
'business_id': None,
|
|
}
|
|
file_reference = SysFileReference(
|
|
reference_id=1,
|
|
file_id=FILE_ID,
|
|
business_type='contract',
|
|
business_id='100',
|
|
business_name='Contract',
|
|
create_by='admin',
|
|
create_time=create_time,
|
|
)
|
|
with (
|
|
patch.object(
|
|
FileInfoDao,
|
|
'get_file_management_detail_by_id',
|
|
new=AsyncMock(return_value=file_info),
|
|
),
|
|
patch.object(
|
|
FileReferenceDao,
|
|
'get_file_reference_list',
|
|
new=AsyncMock(return_value=[file_reference]),
|
|
),
|
|
):
|
|
result = asyncio.run(
|
|
FileReferenceService.get_file_reference_list_services(
|
|
make_query_db(),
|
|
FILE_ID,
|
|
true(),
|
|
)
|
|
)
|
|
|
|
assert result[0].model_dump(by_alias=True) == {
|
|
'referenceId': 1,
|
|
'fileId': FILE_ID,
|
|
'businessType': 'contract',
|
|
'businessId': '100',
|
|
'businessName': 'Contract',
|
|
'retentionExpireTime': None,
|
|
'createBy': 'admin',
|
|
'createTime': create_time,
|
|
'legacy': False,
|
|
}
|
|
|
|
|
|
def test_replace_business_file_references_locks_files_without_committing() -> None:
|
|
query_db = make_query_db()
|
|
file_infos = [SimpleNamespace(file_id=FILE_ID)]
|
|
with (
|
|
patch.object(
|
|
FileInfoDao,
|
|
'get_file_infos_by_ids_for_update',
|
|
new=AsyncMock(return_value=file_infos),
|
|
) as get_file_infos,
|
|
patch.object(
|
|
FileReferenceDao,
|
|
'replace_business_file_references',
|
|
new_callable=AsyncMock,
|
|
) as replace_references,
|
|
patch.object(
|
|
FileRetentionPolicyService,
|
|
'get_enabled_file_retention_policy_services',
|
|
new=AsyncMock(return_value=None),
|
|
),
|
|
):
|
|
asyncio.run(
|
|
FileReferenceService.replace_business_file_references_services(
|
|
query_db,
|
|
'notice',
|
|
'10',
|
|
[FILE_ID, FILE_ID],
|
|
create_by='admin',
|
|
file_data_scope_sql=true(),
|
|
business_name='系统公告',
|
|
)
|
|
)
|
|
|
|
assert get_file_infos.await_args.args[1] == [FILE_ID]
|
|
reference_list = replace_references.await_args.args[3]
|
|
assert len(reference_list) == 1
|
|
assert isinstance(reference_list[0], SysFileReference)
|
|
assert reference_list[0].file_id == FILE_ID
|
|
assert reference_list[0].business_type == 'notice'
|
|
assert reference_list[0].business_id == '10'
|
|
assert reference_list[0].business_name == '系统公告'
|
|
query_db.commit.assert_not_awaited()
|
|
|
|
|
|
def test_replace_business_file_references_rejects_invalid_file() -> None:
|
|
query_db = make_query_db()
|
|
with (
|
|
patch.object(FileInfoDao, 'get_file_infos_by_ids_for_update', new=AsyncMock(return_value=[])),
|
|
patch.object(
|
|
FileReferenceDao,
|
|
'replace_business_file_references',
|
|
new_callable=AsyncMock,
|
|
) as replace_references,
|
|
pytest.raises(ServiceException) as file_error,
|
|
):
|
|
asyncio.run(
|
|
FileReferenceService.replace_business_file_references_services(
|
|
query_db,
|
|
'notice',
|
|
'10',
|
|
[FILE_ID],
|
|
create_by='admin',
|
|
file_data_scope_sql=true(),
|
|
)
|
|
)
|
|
|
|
assert file_error.value.message == '部分引用文件不存在或已失效'
|
|
replace_references.assert_not_awaited()
|
|
|
|
|
|
def test_remove_business_file_references_does_not_lock_files() -> None:
|
|
query_db = make_query_db()
|
|
with (
|
|
patch.object(
|
|
FileReferenceDao,
|
|
'replace_business_file_references',
|
|
new_callable=AsyncMock,
|
|
) as replace_references,
|
|
patch.object(
|
|
FileRetentionPolicyService,
|
|
'get_enabled_file_retention_policy_services',
|
|
new=AsyncMock(return_value=None),
|
|
),
|
|
):
|
|
asyncio.run(
|
|
FileReferenceService.remove_business_file_references_services(
|
|
query_db,
|
|
'notice',
|
|
'10',
|
|
)
|
|
)
|
|
|
|
assert replace_references.await_args.args[3] == []
|
|
query_db.commit.assert_not_awaited()
|
|
|
|
|
|
def test_replace_business_file_references_applies_retention_policy() -> None:
|
|
query_db = make_query_db()
|
|
create_time = datetime(2026, 7, 23, 10, 0, 0, tzinfo=timezone.utc)
|
|
policy = FileRetentionPolicyModel(businessType='notice', retentionDays=30)
|
|
with (
|
|
patch.object(
|
|
FileInfoDao,
|
|
'get_file_infos_by_ids_for_update',
|
|
new=AsyncMock(return_value=[SimpleNamespace(file_id=FILE_ID)]),
|
|
),
|
|
patch.object(
|
|
FileRetentionPolicyService,
|
|
'get_enabled_file_retention_policy_services',
|
|
new=AsyncMock(return_value=policy),
|
|
),
|
|
patch.object(
|
|
FileReferenceDao,
|
|
'replace_business_file_references',
|
|
new_callable=AsyncMock,
|
|
) as replace_references,
|
|
patch('module_admin.service.file_business_service.TimezoneUtil.utc_now', return_value=create_time),
|
|
):
|
|
asyncio.run(
|
|
FileReferenceService.replace_business_file_references_services(
|
|
query_db,
|
|
'notice',
|
|
'10',
|
|
[FILE_ID],
|
|
create_by='admin',
|
|
file_data_scope_sql=true(),
|
|
)
|
|
)
|
|
|
|
reference = replace_references.await_args.args[3][0]
|
|
assert reference.retention_expire_time == create_time + timedelta(days=30)
|
|
|
|
|
|
def test_replace_business_file_references_preserves_extended_expiration() -> None:
|
|
policy_expire_time = datetime(2026, 8, 22, 10, 0, 0, tzinfo=timezone.utc)
|
|
extended_expire_time = datetime(2027, 7, 23, 10, 0, 0, tzinfo=timezone.utc)
|
|
old_reference = SysFileReference(
|
|
file_id=FILE_ID,
|
|
business_type='notice',
|
|
business_id='10',
|
|
retention_expire_time=extended_expire_time,
|
|
)
|
|
new_reference = SysFileReference(
|
|
file_id=FILE_ID,
|
|
business_type='notice',
|
|
business_id='10',
|
|
retention_expire_time=policy_expire_time,
|
|
)
|
|
|
|
FileReferenceDao._preserve_later_retention_expire_times(
|
|
[old_reference],
|
|
[new_reference],
|
|
)
|
|
|
|
assert new_reference.retention_expire_time == extended_expire_time
|
|
|
|
|
|
def test_refresh_file_expire_times_uses_latest_reference_expiration() -> None:
|
|
first_expire_time = datetime(2026, 8, 1, tzinfo=timezone.utc)
|
|
last_expire_time = datetime(2026, 9, 1, tzinfo=timezone.utc)
|
|
file_info = SimpleNamespace(
|
|
file_id=FILE_ID,
|
|
business_type=None,
|
|
business_id=None,
|
|
expire_time=None,
|
|
)
|
|
query_db = SimpleNamespace(
|
|
execute=AsyncMock(
|
|
return_value=SimpleNamespace(
|
|
all=lambda: [
|
|
(FILE_ID, first_expire_time),
|
|
(FILE_ID, last_expire_time),
|
|
]
|
|
)
|
|
)
|
|
)
|
|
|
|
asyncio.run(
|
|
FileReferenceDao._refresh_file_expire_times(
|
|
query_db,
|
|
[FILE_ID],
|
|
{FILE_ID: file_info},
|
|
)
|
|
)
|
|
|
|
assert file_info.expire_time == last_expire_time
|