Merge pull request #2635 from langbot-app/test/core-contract-coverage-gaps

test(core): verify runtime behavior and close PostgreSQL coverage gaps
This commit is contained in:
huanghuoguoguo
2026-10-17 09:13:07 +08:00
committed by GitHub
parent 430276a5fc
commit 2b79b04d36
9 changed files with 502 additions and 316 deletions
@@ -867,7 +867,8 @@ class TestBotServiceSendMessage:
with pytest.raises(Exception, match='Invalid message_chain format'):
await service.send_message(WORKSPACE_UUID, 'bot-uuid', 'group', '123', {'invalid': 'format'})
async def test_send_message_valid_call(self):
@pytest.mark.parametrize(('target_type', 'target_id'), [('group', 123), ('person', 'user-456')])
async def test_send_message_valid_call(self, target_type, target_id):
"""Sends message through adapter when all valid."""
# Setup
ap = SimpleNamespace()
@@ -881,14 +882,18 @@ class TestBotServiceSendMessage:
service = BotService(ap)
service.get_bot = AsyncMock(return_value={'uuid': 'bot-uuid'})
# Execute with valid message chain format
message_chain_data = {'messages': [{'type': 'text', 'data': {'text': 'Hello'}}]}
from langbot_plugin.api.entities.builtin.platform.message import MessageChain, Plain
# Patch the import location - the module imports inside the function
with patch('langbot_plugin.api.entities.builtin.platform.message.MessageChain') as MockMessageChain:
mock_chain = Mock()
MockMessageChain.model_validate = Mock(return_value=mock_chain)
await service.send_message(WORKSPACE_UUID, 'bot-uuid', 'group', '123', message_chain_data)
expected_chain = MessageChain([Plain(text='Hello')])
message_chain_data = expected_chain.model_dump()
await service.send_message(WORKSPACE_UUID, 'bot-uuid', target_type, target_id, message_chain_data)
# Verify adapter.send_message was called
runtime_bot.adapter.send_message.assert_called_once_with('group', '123', mock_chain)
service.get_bot.assert_awaited_once_with(WORKSPACE_UUID, 'bot-uuid', include_secret=False)
ap.platform_mgr.get_bot_by_uuid.assert_awaited_once_with(WORKSPACE_UUID, 'bot-uuid')
runtime_bot.adapter.send_message.assert_awaited_once()
sent_target_type, sent_target_id, sent_chain = runtime_bot.adapter.send_message.await_args.args
assert sent_target_type == target_type
assert sent_target_id == str(target_id)
assert isinstance(sent_chain, MessageChain)
assert sent_chain.model_dump() == expected_chain.model_dump()
assert str(sent_chain) == 'Hello'
@@ -16,6 +16,10 @@ from __future__ import annotations
import pytest
import jwt
import datetime
import sqlalchemy
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError
from unittest.mock import AsyncMock, Mock
from types import SimpleNamespace
@@ -510,10 +514,17 @@ class TestUserServiceResetPassword:
service = UserService(ap)
# Execute
await service.reset_password('test@example.com', 'new_password')
await service.reset_password(' Test@Example.COM ', 'new_password')
# Verify - execute_async was called with update
ap.persistence_mgr.execute_async.assert_called_once()
ap.persistence_mgr.execute_async.assert_awaited_once()
statement = ap.persistence_mgr.execute_async.await_args.args[0]
assert isinstance(statement, sqlalchemy.sql.dml.Update)
assert statement.table.name == User.__tablename__
params = statement.compile().params
assert params['normalized_email_1'] == '[email protected]'
assert PasswordHasher().verify(params['password'], 'new_password')
with pytest.raises(VerifyMismatchError):
PasswordHasher().verify(params['password'], 'old-password')
class TestUserServiceChangePassword:
@@ -377,104 +377,59 @@ class TestWebhookServiceGetWebhook:
class TestWebhookServiceUpdateWebhook:
"""Tests for update_webhook method."""
async def test_update_webhook_name_only(self):
"""Updates only the name field."""
# Setup
ap = SimpleNamespace()
ap.persistence_mgr = SimpleNamespace()
ap.persistence_mgr.execute_async = AsyncMock(return_value=_create_write_result())
@pytest.mark.parametrize(
'changes',
[
{'name': 'Updated Name'},
{'url': 'https://updated.example.com'},
{'description': 'Updated description'},
{'enabled': False},
{
'name': 'All Updated',
'url': 'https://all.updated.example.com',
'description': 'All updated description',
'enabled': False,
},
],
ids=['name-only', 'url-only', 'description-only', 'enabled-only', 'all-fields'],
)
async def test_update_webhook_persists_requested_fields(self, tenant_webhook_service, changes):
"""Persist requested fields without changing omitted fields or another Workspace."""
original = {
'name': 'Original Name',
'url': 'https://original.example.com',
'description': 'Original description',
'enabled': True,
}
created = await tenant_webhook_service.create_webhook(ISOLATION_WORKSPACE_A, **original)
other = await tenant_webhook_service.create_webhook(ISOLATION_WORKSPACE_B, **original)
service = WebhookService(ap)
assert await tenant_webhook_service.update_webhook(ISOLATION_WORKSPACE_A, created['id'], **changes) is True
# Execute
await service.update_webhook(WORKSPACE_UUID, 1, name='Updated Name')
updated = await tenant_webhook_service.get_webhook(ISOLATION_WORKSPACE_A, created['id'], include_secret=True)
assert {field: updated[field] for field in original} == original | changes
assert updated['id'] == created['id']
assert updated['workspace_uuid'] == ISOLATION_WORKSPACE_A
assert (
await tenant_webhook_service.get_webhook(ISOLATION_WORKSPACE_B, other['id'], include_secret=True) == other
)
# Verify
ap.persistence_mgr.execute_async.assert_called_once()
async def test_update_webhook_url_only(self):
"""Updates only the url field."""
# Setup
ap = SimpleNamespace()
ap.persistence_mgr = SimpleNamespace()
ap.persistence_mgr.execute_async = AsyncMock(return_value=_create_write_result())
service = WebhookService(ap)
# Execute
await service.update_webhook(WORKSPACE_UUID, 1, url='http://updated.example.com')
# Verify
ap.persistence_mgr.execute_async.assert_called_once()
async def test_update_webhook_description_only(self):
"""Updates only the description field."""
# Setup
ap = SimpleNamespace()
ap.persistence_mgr = SimpleNamespace()
ap.persistence_mgr.execute_async = AsyncMock(return_value=_create_write_result())
service = WebhookService(ap)
# Execute
await service.update_webhook(WORKSPACE_UUID, 1, description='Updated description')
# Verify
ap.persistence_mgr.execute_async.assert_called_once()
async def test_update_webhook_enabled_only(self):
"""Updates only the enabled field."""
# Setup
ap = SimpleNamespace()
ap.persistence_mgr = SimpleNamespace()
ap.persistence_mgr.execute_async = AsyncMock(return_value=_create_write_result())
service = WebhookService(ap)
# Execute
await service.update_webhook(WORKSPACE_UUID, 1, enabled=False)
# Verify
ap.persistence_mgr.execute_async.assert_called_once()
async def test_update_webhook_all_fields(self):
"""Updates all fields at once."""
# Setup
ap = SimpleNamespace()
ap.persistence_mgr = SimpleNamespace()
ap.persistence_mgr.execute_async = AsyncMock(return_value=_create_write_result())
service = WebhookService(ap)
# Execute
await service.update_webhook(
WORKSPACE_UUID,
1,
name='All Updated',
url='http://all.updated.com',
description='All updated description',
async def test_update_webhook_no_fields(self, tenant_webhook_service):
"""An empty update reports scoped existence and leaves persisted values unchanged."""
created = await tenant_webhook_service.create_webhook(
ISOLATION_WORKSPACE_A,
'Unchanged',
'https://unchanged.example.com',
description='Keep this description',
enabled=False,
)
# Verify
ap.persistence_mgr.execute_async.assert_called_once()
async def test_update_webhook_no_fields(self):
"""Does nothing when no fields provided."""
# Setup
ap = SimpleNamespace()
ap.persistence_mgr = SimpleNamespace()
existing = _create_mock_webhook(webhook_id=1)
ap.persistence_mgr.execute_async = AsyncMock(return_value=_create_mock_result(first_item=existing))
ap.persistence_mgr.serialize_model = Mock(return_value={'id': 1})
service = WebhookService(ap)
# Execute - no update parameters
await service.update_webhook(WORKSPACE_UUID, 1)
# No write is issued; one scoped existence lookup is performed.
ap.persistence_mgr.execute_async.assert_called_once()
assert await tenant_webhook_service.update_webhook(ISOLATION_WORKSPACE_A, created['id']) is True
assert (
await tenant_webhook_service.get_webhook(ISOLATION_WORKSPACE_A, created['id'], include_secret=True)
== created
)
assert await tenant_webhook_service.update_webhook(ISOLATION_WORKSPACE_B, created['id']) is False
class TestWebhookServiceDeleteWebhook:
@@ -695,6 +650,10 @@ async def test_update_and_delete_are_scoped(tenant_webhook_service):
name='new',
enabled=False,
)
updated = await tenant_webhook_service.get_webhook(ISOLATION_WORKSPACE_A, created['id'], include_secret=True)
assert updated['name'] == 'new'
assert updated['enabled'] is False
assert updated['url'] == 'https://a.invalid/old'
assert await tenant_webhook_service.get_enabled_webhooks(ISOLATION_WORKSPACE_A) == []
assert await tenant_webhook_service.delete_webhook(ISOLATION_WORKSPACE_A, created['id'])
assert await tenant_webhook_service.get_webhook(ISOLATION_WORKSPACE_A, created['id']) is None