Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/1184.changed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Deferred the missing python-magic warning in pysnow to only emit when attachment upload is attempted, rather than at import time.
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@
HAS_MAGIC = True
except ImportError: # pragma: no cover
HAS_MAGIC = False
warnings.warn(
"Missing the python-magic library; attachment content-type will be set to text/plain. "
"Try installing the python-magic-bin package if running on Mac or Windows"
)

from .exceptions import InvalidUsage

Expand Down Expand Up @@ -68,7 +64,14 @@ def upload(self, sys_id, file_path, name=None, multipart=False):
headers["Content-Type"] = "multipart/form-data"
path_append = "/upload"
else:
headers["Content-Type"] = magic.from_file(file_path, mime=True) if HAS_MAGIC else "text/plain"
if HAS_MAGIC:
headers["Content-Type"] = magic.from_file(file_path, mime=True)
else:
warnings.warn(
"Missing the python-magic library; attachment content-type will be set to text/plain. "
"Try installing the python-magic-bin package if running on Mac or Windows"
)
headers["Content-Type"] = "text/plain"
path_append = "/file"

return resource.request(method="POST", data=data, headers=headers, path_append=path_append)
Expand Down
45 changes: 45 additions & 0 deletions nautobot_ssot/tests/servicenow/test_pysnow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""Unit tests for the pysnow module."""

import warnings
from unittest.mock import MagicMock, mock_open, patch

from nautobot.core.testing import TestCase

from nautobot_ssot.integrations.servicenow.third_party.pysnow.attachment import Attachment


class TestAttachmentUploadContentType(TestCase):
"""Test that the upload method sets Content-Type based on HAS_MAGIC."""

def setUp(self):
"""Set up a minimal Attachment instance with a mocked resource."""
self.resource = MagicMock()
self.attachment = Attachment(resource=self.resource, table_name="incident")

@patch("nautobot_ssot.integrations.servicenow.third_party.pysnow.attachment.HAS_MAGIC", True)
@patch("nautobot_ssot.integrations.servicenow.third_party.pysnow.attachment.magic", create=True)
@patch("builtins.open", mock_open(read_data=b"file content"))
def test_upload_uses_magic_when_available(self, mock_magic):
"""When python-magic is available, Content-Type should be determined by magic."""
file_path = "/tmp/test.pdf"
mock_magic.from_file.return_value = "application/pdf"

self.attachment.upload(sys_id="abc123", file_path=file_path)

mock_magic.from_file.assert_called_once_with(file_path, mime=True)
call_kwargs = self.resource.request.call_args[1]
self.assertEqual(call_kwargs["headers"]["Content-Type"], "application/pdf")

@patch("nautobot_ssot.integrations.servicenow.third_party.pysnow.attachment.HAS_MAGIC", False)
@patch("builtins.open", mock_open(read_data=b"file content"))
def test_upload_falls_back_to_text_plain_without_magic(self):
"""When python-magic is missing, Content-Type should fall back to text/plain with a warning."""
with warnings.catch_warnings(record=True) as caught_warnings:
warnings.simplefilter("always")
self.attachment.upload(sys_id="abc123", file_path="/tmp/test.txt")

self.assertEqual(len(caught_warnings), 1)
self.assertIn("python-magic", str(caught_warnings[0].message))

call_kwargs = self.resource.request.call_args[1]
self.assertEqual(call_kwargs["headers"]["Content-Type"], "text/plain")
Loading