test: migrate test_document_service_rename_document SQL tests to testcontainers (#32542)

Co-authored-by: KinomotoMio <200703522+KinomotoMio@users.noreply.github.com>
This commit is contained in:
木之本澪 2026-03-05 09:07:29 +08:00 committed by GitHub
parent 2977a4d2a4
commit 2b5ce196ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 252 additions and 176 deletions

View File

@ -0,0 +1,252 @@
"""Container-backed integration tests for DocumentService.rename_document real SQL paths."""
import datetime
import json
from unittest.mock import create_autospec, patch
from uuid import uuid4
import pytest
from models import Account
from models.dataset import Dataset, Document
from models.enums import CreatorUserRole
from models.model import UploadFile
from services.dataset_service import DocumentService
FIXED_UPLOAD_CREATED_AT = datetime.datetime(2024, 1, 1, 0, 0, 0)
@pytest.fixture
def mock_env():
"""Patch only non-SQL dependency used by rename_document: current_user context."""
with patch("services.dataset_service.current_user", create_autospec(Account, instance=True)) as current_user:
current_user.current_tenant_id = str(uuid4())
current_user.id = str(uuid4())
yield {"current_user": current_user}
def make_dataset(db_session_with_containers, dataset_id=None, tenant_id=None, built_in_field_enabled=False):
"""Persist a dataset row for rename_document integration scenarios."""
dataset_id = dataset_id or str(uuid4())
tenant_id = tenant_id or str(uuid4())
dataset = Dataset(
tenant_id=tenant_id,
name=f"dataset-{uuid4()}",
data_source_type="upload_file",
created_by=str(uuid4()),
)
dataset.id = dataset_id
dataset.built_in_field_enabled = built_in_field_enabled
db_session_with_containers.add(dataset)
db_session_with_containers.commit()
return dataset
def make_document(
db_session_with_containers,
document_id=None,
dataset_id=None,
tenant_id=None,
name="Old Name",
data_source_info=None,
doc_metadata=None,
):
"""Persist a document row used by rename_document integration scenarios."""
document_id = document_id or str(uuid4())
dataset_id = dataset_id or str(uuid4())
tenant_id = tenant_id or str(uuid4())
doc = Document(
tenant_id=tenant_id,
dataset_id=dataset_id,
position=1,
data_source_type="upload_file",
data_source_info=json.dumps(data_source_info or {}),
batch=f"batch-{uuid4()}",
name=name,
created_from="web",
created_by=str(uuid4()),
doc_form="text_model",
)
doc.id = document_id
doc.indexing_status = "completed"
doc.doc_metadata = dict(doc_metadata or {})
db_session_with_containers.add(doc)
db_session_with_containers.commit()
return doc
def make_upload_file(db_session_with_containers, tenant_id: str, file_id: str, name: str):
"""Persist an upload file row referenced by document.data_source_info."""
upload_file = UploadFile(
tenant_id=tenant_id,
storage_type="local",
key=f"uploads/{uuid4()}",
name=name,
size=128,
extension="pdf",
mime_type="application/pdf",
created_by_role=CreatorUserRole.ACCOUNT,
created_by=str(uuid4()),
created_at=FIXED_UPLOAD_CREATED_AT,
used=False,
)
upload_file.id = file_id
db_session_with_containers.add(upload_file)
db_session_with_containers.commit()
return upload_file
def test_rename_document_success(db_session_with_containers, mock_env):
"""Rename succeeds and returns the renamed document identity by id."""
# Arrange
dataset_id = str(uuid4())
document_id = str(uuid4())
new_name = "New Document Name"
dataset = make_dataset(db_session_with_containers, dataset_id, mock_env["current_user"].current_tenant_id)
document = make_document(
db_session_with_containers,
document_id=document_id,
dataset_id=dataset_id,
tenant_id=mock_env["current_user"].current_tenant_id,
)
# Act
result = DocumentService.rename_document(dataset.id, document_id, new_name)
# Assert
db_session_with_containers.refresh(document)
assert result.id == document.id
assert document.name == new_name
def test_rename_document_with_built_in_fields(db_session_with_containers, mock_env):
"""Built-in document_name metadata is updated while existing metadata keys are preserved."""
# Arrange
dataset_id = str(uuid4())
document_id = str(uuid4())
new_name = "Renamed"
dataset = make_dataset(
db_session_with_containers,
dataset_id,
mock_env["current_user"].current_tenant_id,
built_in_field_enabled=True,
)
document = make_document(
db_session_with_containers,
document_id=document_id,
dataset_id=dataset.id,
tenant_id=mock_env["current_user"].current_tenant_id,
doc_metadata={"foo": "bar"},
)
# Act
DocumentService.rename_document(dataset.id, document.id, new_name)
# Assert
db_session_with_containers.refresh(document)
assert document.name == new_name
assert document.doc_metadata["document_name"] == new_name
assert document.doc_metadata["foo"] == "bar"
def test_rename_document_updates_upload_file_when_present(db_session_with_containers, mock_env):
"""Rename propagates to UploadFile.name when upload_file_id is present in data_source_info."""
# Arrange
dataset_id = str(uuid4())
document_id = str(uuid4())
file_id = str(uuid4())
new_name = "Renamed"
dataset = make_dataset(db_session_with_containers, dataset_id, mock_env["current_user"].current_tenant_id)
document = make_document(
db_session_with_containers,
document_id=document_id,
dataset_id=dataset.id,
tenant_id=mock_env["current_user"].current_tenant_id,
data_source_info={"upload_file_id": file_id},
)
upload_file = make_upload_file(
db_session_with_containers,
tenant_id=mock_env["current_user"].current_tenant_id,
file_id=file_id,
name="old.pdf",
)
# Act
DocumentService.rename_document(dataset.id, document.id, new_name)
# Assert
db_session_with_containers.refresh(document)
db_session_with_containers.refresh(upload_file)
assert document.name == new_name
assert upload_file.name == new_name
def test_rename_document_does_not_update_upload_file_when_missing_id(db_session_with_containers, mock_env):
"""Rename does not update UploadFile when data_source_info lacks upload_file_id."""
# Arrange
dataset_id = str(uuid4())
document_id = str(uuid4())
new_name = "Another Name"
dataset = make_dataset(db_session_with_containers, dataset_id, mock_env["current_user"].current_tenant_id)
document = make_document(
db_session_with_containers,
document_id=document_id,
dataset_id=dataset.id,
tenant_id=mock_env["current_user"].current_tenant_id,
data_source_info={"url": "https://example.com"},
)
untouched_file = make_upload_file(
db_session_with_containers,
tenant_id=mock_env["current_user"].current_tenant_id,
file_id=str(uuid4()),
name="untouched.pdf",
)
# Act
DocumentService.rename_document(dataset.id, document.id, new_name)
# Assert
db_session_with_containers.refresh(document)
db_session_with_containers.refresh(untouched_file)
assert document.name == new_name
assert untouched_file.name == "untouched.pdf"
def test_rename_document_dataset_not_found(db_session_with_containers, mock_env):
"""Rename raises Dataset not found when dataset id does not exist."""
# Arrange
missing_dataset_id = str(uuid4())
# Act / Assert
with pytest.raises(ValueError, match="Dataset not found"):
DocumentService.rename_document(missing_dataset_id, str(uuid4()), "x")
def test_rename_document_not_found(db_session_with_containers, mock_env):
"""Rename raises Document not found when document id is absent in the dataset."""
# Arrange
dataset = make_dataset(db_session_with_containers, str(uuid4()), mock_env["current_user"].current_tenant_id)
# Act / Assert
with pytest.raises(ValueError, match="Document not found"):
DocumentService.rename_document(dataset.id, str(uuid4()), "x")
def test_rename_document_permission_denied_when_tenant_mismatch(db_session_with_containers, mock_env):
"""Rename raises No permission when document tenant differs from current_user tenant."""
# Arrange
dataset = make_dataset(db_session_with_containers, str(uuid4()), mock_env["current_user"].current_tenant_id)
document = make_document(
db_session_with_containers,
dataset_id=dataset.id,
tenant_id=str(uuid4()),
)
# Act / Assert
with pytest.raises(ValueError, match="No permission"):
DocumentService.rename_document(dataset.id, document.id, "x")

View File

@ -1,176 +0,0 @@
from types import SimpleNamespace
from unittest.mock import Mock, create_autospec, patch
import pytest
from models import Account
from services.dataset_service import DocumentService
@pytest.fixture
def mock_env():
"""Patch dependencies used by DocumentService.rename_document.
Mocks:
- DatasetService.get_dataset
- DocumentService.get_document
- current_user (with current_tenant_id)
- db.session
"""
with (
patch("services.dataset_service.DatasetService.get_dataset") as get_dataset,
patch("services.dataset_service.DocumentService.get_document") as get_document,
patch("services.dataset_service.current_user", create_autospec(Account, instance=True)) as current_user,
patch("extensions.ext_database.db.session") as db_session,
):
current_user.current_tenant_id = "tenant-123"
yield {
"get_dataset": get_dataset,
"get_document": get_document,
"current_user": current_user,
"db_session": db_session,
}
def make_dataset(dataset_id="dataset-123", tenant_id="tenant-123", built_in_field_enabled=False):
return SimpleNamespace(id=dataset_id, tenant_id=tenant_id, built_in_field_enabled=built_in_field_enabled)
def make_document(
document_id="document-123",
dataset_id="dataset-123",
tenant_id="tenant-123",
name="Old Name",
data_source_info=None,
doc_metadata=None,
):
doc = Mock()
doc.id = document_id
doc.dataset_id = dataset_id
doc.tenant_id = tenant_id
doc.name = name
doc.data_source_info = data_source_info or {}
# property-like usage in code relies on a dict
doc.data_source_info_dict = dict(doc.data_source_info)
doc.doc_metadata = dict(doc_metadata or {})
return doc
def test_rename_document_success(mock_env):
dataset_id = "dataset-123"
document_id = "document-123"
new_name = "New Document Name"
dataset = make_dataset(dataset_id)
document = make_document(document_id=document_id, dataset_id=dataset_id)
mock_env["get_dataset"].return_value = dataset
mock_env["get_document"].return_value = document
result = DocumentService.rename_document(dataset_id, document_id, new_name)
assert result is document
assert document.name == new_name
mock_env["db_session"].add.assert_called_once_with(document)
mock_env["db_session"].commit.assert_called_once()
def test_rename_document_with_built_in_fields(mock_env):
dataset_id = "dataset-123"
document_id = "document-123"
new_name = "Renamed"
dataset = make_dataset(dataset_id, built_in_field_enabled=True)
document = make_document(document_id=document_id, dataset_id=dataset_id, doc_metadata={"foo": "bar"})
mock_env["get_dataset"].return_value = dataset
mock_env["get_document"].return_value = document
DocumentService.rename_document(dataset_id, document_id, new_name)
assert document.name == new_name
# BuiltInField.document_name == "document_name" in service code
assert document.doc_metadata["document_name"] == new_name
assert document.doc_metadata["foo"] == "bar"
def test_rename_document_updates_upload_file_when_present(mock_env):
dataset_id = "dataset-123"
document_id = "document-123"
new_name = "Renamed"
file_id = "file-123"
dataset = make_dataset(dataset_id)
document = make_document(
document_id=document_id,
dataset_id=dataset_id,
data_source_info={"upload_file_id": file_id},
)
mock_env["get_dataset"].return_value = dataset
mock_env["get_document"].return_value = document
# Intercept UploadFile rename UPDATE chain
mock_query = Mock()
mock_query.where.return_value = mock_query
mock_env["db_session"].query.return_value = mock_query
DocumentService.rename_document(dataset_id, document_id, new_name)
assert document.name == new_name
mock_env["db_session"].query.assert_called() # update executed
def test_rename_document_does_not_update_upload_file_when_missing_id(mock_env):
"""
When data_source_info_dict exists but does not contain "upload_file_id",
UploadFile should not be updated.
"""
dataset_id = "dataset-123"
document_id = "document-123"
new_name = "Another Name"
dataset = make_dataset(dataset_id)
# Ensure data_source_info_dict is truthy but lacks the key
document = make_document(
document_id=document_id,
dataset_id=dataset_id,
data_source_info={"url": "https://example.com"},
)
mock_env["get_dataset"].return_value = dataset
mock_env["get_document"].return_value = document
DocumentService.rename_document(dataset_id, document_id, new_name)
assert document.name == new_name
# Should NOT attempt to update UploadFile
mock_env["db_session"].query.assert_not_called()
def test_rename_document_dataset_not_found(mock_env):
mock_env["get_dataset"].return_value = None
with pytest.raises(ValueError, match="Dataset not found"):
DocumentService.rename_document("missing", "doc", "x")
def test_rename_document_not_found(mock_env):
dataset = make_dataset("dataset-123")
mock_env["get_dataset"].return_value = dataset
mock_env["get_document"].return_value = None
with pytest.raises(ValueError, match="Document not found"):
DocumentService.rename_document(dataset.id, "missing", "x")
def test_rename_document_permission_denied_when_tenant_mismatch(mock_env):
dataset = make_dataset("dataset-123")
# different tenant than current_user.current_tenant_id
document = make_document(dataset_id=dataset.id, tenant_id="tenant-other")
mock_env["get_dataset"].return_value = dataset
mock_env["get_document"].return_value = document
with pytest.raises(ValueError, match="No permission"):
DocumentService.rename_document(dataset.id, document.id, "x")