contact: warn on a PHOTO of no known image format
The sniff's nil was the last silent failure: a format the signature
list misses, or junk that happened to decode, showed as initials with
no trace.

Assisted-by: GLM-5.3 via pi
change qorkpxmwylslqyplssowpnyvwnvkwnnu
commit de1ef0663d640f737ca3f77ab17046863cc68341
author Alpha Chen <alpha@kejadlen.dev>
date
parent syyprrzl
diff --git a/lib/pro_tacts/contact.rb b/lib/pro_tacts/contact.rb
index f48d513..aff457d 100644
--- a/lib/pro_tacts/contact.rb
+++ b/lib/pro_tacts/contact.rb
@@ -162,11 +162,14 @@ module ProTacts
     # PHOTO, an undecodable one, or one that sniffs to no known format
     # — the initials an avatar falls back to, an ordinary absence
     # rather than an error, exactly like the other accessors' nils.
-    # The undecodable nil is the one departure, and reports on its way
-    # there — warning-grade, carrying no card content — because a
-    # PHOTO this server cannot read is news, the URI form included:
-    # legal vCard (RFC 2426 section 3.1.4) that no client here sends
-    # yet. At the read rather than the arrival, unlike the parser's
+    # The two failures among those nils report on the way there —
+    # warning-grade, carrying no card content — because a PHOTO this
+    # server cannot read is news either way it fails: a decode
+    # refusal could be the URI form, legal vCard (RFC 2426 section
+    # 3.1.4) that no client here sends yet, and a decode that sniffs
+    # to nothing is either junk that happened to decode or an image
+    # format the signature list does not cover yet. At the read
+    # rather than the arrival, unlike the parser's
     # reports (Web#report_broken_assumptions): no arrival probe reads
     # PHOTO, and a stored card can predate the report, while the
     # decode runs only where an avatar renders and Sentry groups the
@@ -184,7 +187,13 @@ module ProTacts
       end
 
       signature = PHOTO_SIGNATURES.find { bytes.start_with?(it.first) }
-      return if signature.nil?
+      if signature.nil?
+        Sentry.capture_message(
+          "a card's PHOTO decoded to no image format this server recognizes",
+          level: :warning,
+        )
+        return
+      end
 
       Photo.new(mime_type: signature.fetch(1), bytes:)
     end
diff --git a/test/pro_tacts/test_contact.rb b/test/pro_tacts/test_contact.rb
index abeb54e..9e355c8 100644
--- a/test/pro_tacts/test_contact.rb
+++ b/test/pro_tacts/test_contact.rb
@@ -257,6 +257,9 @@ class ContactTest < Minitest::Test
 
   def test_a_card_without_a_photo_has_none
     assert_nil contact.photo
+
+    # The one nil that stays quiet: no PHOTO is no news.
+    assert_empty sentry_events
   end
 
   # A URI-form PHOTO never reaches the sniff: a URI's ":" is not in
@@ -288,14 +291,17 @@ class ContactTest < Minitest::Test
     assert_equal "ArgumentError", event.exception.values.first.type
   end
 
-  # The other nils stay quiet: an image that decodes but sniffs to no
-  # known format is an allowlist gap with no exception to name, an
-  # ordinary absence exactly like no PHOTO at all.
-  def test_a_photo_of_no_known_format_stays_quiet
+  # The sniff's nil reports too, a message rather than an exception —
+  # there is no error to name, only bytes whose format the signature
+  # list does not know, and the message is the record that they
+  # arrived.
+  def test_a_photo_of_no_known_format_warns
     unknown = CARD.sub("FN:Aiden\r\n", "PHOTO;ENCODING=b:#{Base64.strict_encode64("RIFF____WEBP")}\r\n")
 
     assert_nil contact(unknown).photo
-    assert_empty sentry_events
+
+    assert_equal 1, sentry_messages.length
+    assert_match(/PHOTO decoded to no image format/, sentry_messages.fetch(0))
   end
 
   # One PHOTO property carrying a PNG payload, for the sniff's rows.