feat(platform): close AI expense value loop
Add tenant-safe value, telemetry, connector, commercial, and production-readiness foundations.
This commit is contained in:
@@ -1,78 +1,83 @@
|
||||
import pytest
|
||||
from unittest.mock import MagicMock, patch
|
||||
from io import BytesIO
|
||||
from openpyxl import Workbook
|
||||
from app.services.agent_assets import AgentAssetService
|
||||
from app.schemas.agent_asset import AgentAssetOnlyOfficeCallbackWrite
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
def test_onlyoffice_callback_generates_summary_note():
|
||||
# Setup mock DB and repository
|
||||
from openpyxl import Workbook
|
||||
|
||||
from app.services.agent_asset_onlyoffice_security import (
|
||||
AgentAssetOnlyOfficeValidatedSession,
|
||||
)
|
||||
from app.services.agent_assets import AgentAssetService
|
||||
|
||||
|
||||
def test_onlyoffice_callback_delegates_downloaded_workbook_to_upload():
|
||||
db = MagicMock()
|
||||
service = AgentAssetService(db)
|
||||
service.repository = MagicMock()
|
||||
service.spreadsheet_manager = MagicMock()
|
||||
service._ensure_ready = MagicMock()
|
||||
|
||||
# Mock asset and metadata
|
||||
|
||||
asset = MagicMock()
|
||||
asset.id = "test-asset"
|
||||
asset.name = "测试规则"
|
||||
asset.tenant_id = "tenant-a"
|
||||
asset.scope = "tenant"
|
||||
service._require_spreadsheet_rule = MagicMock(return_value=asset)
|
||||
service._resolve_working_version = MagicMock(return_value="v1")
|
||||
|
||||
base_meta = MagicMock()
|
||||
base_meta.file_name = "test.xlsx"
|
||||
base_meta.storage_key = "old-key"
|
||||
base_meta.checksum = "old-checksum"
|
||||
service._resolve_spreadsheet_version_meta = MagicMock(return_value=("v1", base_meta))
|
||||
|
||||
# Create base workbook
|
||||
base_wb = Workbook()
|
||||
base_ws = base_wb.active
|
||||
base_ws["A1"] = "old value"
|
||||
|
||||
# Mock loading base workbook
|
||||
service._load_spreadsheet_for_compare = MagicMock(return_value=base_wb)
|
||||
service.spreadsheet_manager.resolve_storage_path = MagicMock()
|
||||
|
||||
# Create new content (modified)
|
||||
current_metadata = MagicMock(file_name="test.xlsx", checksum="old-checksum")
|
||||
service._resolve_current_spreadsheet_meta = MagicMock(
|
||||
return_value=("current", current_metadata)
|
||||
)
|
||||
|
||||
new_wb = Workbook()
|
||||
new_ws = new_wb.active
|
||||
new_ws["A1"] = "new value" # 1 cell changed
|
||||
new_ws["B2"] = "added" # 1 more cell changed
|
||||
|
||||
# Mock URL open to return new content
|
||||
new_ws["A1"] = "new value"
|
||||
new_ws["B2"] = "added"
|
||||
new_content_bio = BytesIO()
|
||||
new_wb.save(new_content_bio)
|
||||
new_content = new_content_bio.getvalue()
|
||||
|
||||
with patch("app.services.agent_assets.urlopen") as mock_urlopen:
|
||||
mock_response = MagicMock()
|
||||
mock_response.read.return_value = new_content
|
||||
mock_response.__enter__.return_value = mock_response
|
||||
mock_urlopen.return_value = mock_response
|
||||
|
||||
# Mock upload_rule_spreadsheet
|
||||
document_key = service._build_onlyoffice_document_key("test-asset", current_metadata)
|
||||
claimed = AgentAssetOnlyOfficeValidatedSession(
|
||||
jti="callback-jti",
|
||||
tenant_id="tenant-a",
|
||||
resource_scope="tenant",
|
||||
asset_id="test-asset",
|
||||
document_key=document_key,
|
||||
document_version="current",
|
||||
document_fingerprint="old-checksum",
|
||||
writable=True,
|
||||
actor="username:test_user",
|
||||
status="processing",
|
||||
)
|
||||
session_service = MagicMock()
|
||||
session_service.claim_callback.return_value = claimed
|
||||
service._onlyoffice_session_service = MagicMock(return_value=session_service)
|
||||
|
||||
with patch(
|
||||
"app.services.agent_asset_onlyoffice.download_onlyoffice_document",
|
||||
return_value=new_content,
|
||||
) as secure_download:
|
||||
service.upload_rule_spreadsheet = MagicMock()
|
||||
|
||||
# Execute callback handler
|
||||
payload = {
|
||||
"status": 2,
|
||||
"url": "http://onlyoffice/download",
|
||||
"users": ["test_user"]
|
||||
"key": document_key,
|
||||
}
|
||||
|
||||
|
||||
service.handle_rule_spreadsheet_onlyoffice_callback(
|
||||
"test-asset",
|
||||
version="v1",
|
||||
payload=payload
|
||||
version="current",
|
||||
payload=payload,
|
||||
callback_token="signed-callback-token",
|
||||
)
|
||||
|
||||
# Verify upload_rule_spreadsheet was called with correct change_note
|
||||
service.upload_rule_spreadsheet.assert_called_once()
|
||||
call_args = service.upload_rule_spreadsheet.call_args[1]
|
||||
assert "涉及 1 个 Sheet,共 2 处改动" in call_args["change_note"]
|
||||
assert call_args["actor"] == "test_user"
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__])
|
||||
secure_download.assert_called_once_with(
|
||||
"http://onlyoffice/download",
|
||||
expected_filename="test.xlsx",
|
||||
)
|
||||
service.upload_rule_spreadsheet.assert_called_once_with(
|
||||
"test-asset",
|
||||
filename="test.xlsx",
|
||||
content=new_content,
|
||||
actor="username:test_user",
|
||||
source="onlyoffice",
|
||||
)
|
||||
session_service.finish_callback.assert_called_once_with(
|
||||
"callback-jti",
|
||||
succeeded=True,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user