fix(mattermost): use MIME types for media attachments
Bare strings like "image", "audio", "document" were appended to
media_types, but downstream run.py checks mtype.startswith("image/")
and mtype.startswith("audio/"), which never matched. This caused all
Mattermost file attachments to be silently dropped from vision/STT
processing. Use the actual MIME type from file_info instead.
This commit is contained in:
@@ -572,3 +572,102 @@ class TestMattermostRequirements:
|
||||
monkeypatch.delenv("MATTERMOST_URL", raising=False)
|
||||
from gateway.platforms.mattermost import check_mattermost_requirements
|
||||
assert check_mattermost_requirements() is False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Media type propagation (MIME types, not bare strings)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestMattermostMediaTypes:
|
||||
"""Verify that media_types contains actual MIME types (e.g. 'image/png')
|
||||
rather than bare category strings ('image'), so downstream
|
||||
``mtype.startswith("image/")`` checks in run.py work correctly."""
|
||||
|
||||
def setup_method(self):
|
||||
self.adapter = _make_adapter()
|
||||
self.adapter._bot_user_id = "bot_user_id"
|
||||
self.adapter.handle_message = AsyncMock()
|
||||
|
||||
def _make_event(self, file_ids):
|
||||
post_data = {
|
||||
"id": "post_media",
|
||||
"user_id": "user_123",
|
||||
"channel_id": "chan_456",
|
||||
"message": "file attached",
|
||||
"file_ids": file_ids,
|
||||
}
|
||||
return {
|
||||
"event": "posted",
|
||||
"data": {
|
||||
"post": json.dumps(post_data),
|
||||
"channel_type": "O",
|
||||
"sender_name": "@alice",
|
||||
},
|
||||
}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_image_media_type_is_full_mime(self):
|
||||
"""An image attachment should produce 'image/png', not 'image'."""
|
||||
file_info = {"name": "photo.png", "mime_type": "image/png"}
|
||||
self.adapter._api_get = AsyncMock(return_value=file_info)
|
||||
|
||||
mock_resp = AsyncMock()
|
||||
mock_resp.status = 200
|
||||
mock_resp.read = AsyncMock(return_value=b"\x89PNG fake")
|
||||
mock_resp.__aenter__ = AsyncMock(return_value=mock_resp)
|
||||
mock_resp.__aexit__ = AsyncMock(return_value=False)
|
||||
self.adapter._session = MagicMock()
|
||||
self.adapter._session.get = MagicMock(return_value=mock_resp)
|
||||
|
||||
with patch("gateway.platforms.base.cache_image_from_bytes", return_value="/tmp/photo.png"):
|
||||
await self.adapter._handle_ws_event(self._make_event(["file1"]))
|
||||
|
||||
msg = self.adapter.handle_message.call_args[0][0]
|
||||
assert msg.media_types == ["image/png"]
|
||||
assert msg.media_types[0].startswith("image/")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_audio_media_type_is_full_mime(self):
|
||||
"""An audio attachment should produce 'audio/ogg', not 'audio'."""
|
||||
file_info = {"name": "voice.ogg", "mime_type": "audio/ogg"}
|
||||
self.adapter._api_get = AsyncMock(return_value=file_info)
|
||||
|
||||
mock_resp = AsyncMock()
|
||||
mock_resp.status = 200
|
||||
mock_resp.read = AsyncMock(return_value=b"OGG fake")
|
||||
mock_resp.__aenter__ = AsyncMock(return_value=mock_resp)
|
||||
mock_resp.__aexit__ = AsyncMock(return_value=False)
|
||||
self.adapter._session = MagicMock()
|
||||
self.adapter._session.get = MagicMock(return_value=mock_resp)
|
||||
|
||||
with patch("gateway.platforms.base.cache_audio_from_bytes", return_value="/tmp/voice.ogg"), \
|
||||
patch("gateway.platforms.base.cache_image_from_bytes"), \
|
||||
patch("gateway.platforms.base.cache_document_from_bytes"):
|
||||
await self.adapter._handle_ws_event(self._make_event(["file2"]))
|
||||
|
||||
msg = self.adapter.handle_message.call_args[0][0]
|
||||
assert msg.media_types == ["audio/ogg"]
|
||||
assert msg.media_types[0].startswith("audio/")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_document_media_type_is_full_mime(self):
|
||||
"""A document attachment should produce 'application/pdf', not 'document'."""
|
||||
file_info = {"name": "report.pdf", "mime_type": "application/pdf"}
|
||||
self.adapter._api_get = AsyncMock(return_value=file_info)
|
||||
|
||||
mock_resp = AsyncMock()
|
||||
mock_resp.status = 200
|
||||
mock_resp.read = AsyncMock(return_value=b"PDF fake")
|
||||
mock_resp.__aenter__ = AsyncMock(return_value=mock_resp)
|
||||
mock_resp.__aexit__ = AsyncMock(return_value=False)
|
||||
self.adapter._session = MagicMock()
|
||||
self.adapter._session.get = MagicMock(return_value=mock_resp)
|
||||
|
||||
with patch("gateway.platforms.base.cache_document_from_bytes", return_value="/tmp/report.pdf"), \
|
||||
patch("gateway.platforms.base.cache_image_from_bytes"):
|
||||
await self.adapter._handle_ws_event(self._make_event(["file3"]))
|
||||
|
||||
msg = self.adapter.handle_message.call_args[0][0]
|
||||
assert msg.media_types == ["application/pdf"]
|
||||
assert not msg.media_types[0].startswith("image/")
|
||||
assert not msg.media_types[0].startswith("audio/")
|
||||
|
||||
Reference in New Issue
Block a user