Skip empty Thumbnail in galleryThumbs + hidden-embed gallery test

EmbedGallery_ViewImage.Thumbnail is a non-pointer string (unlike
EmbedVideo_View.Thumbnail), so an empty value would slip through as
<meta property="og:image" content="">. Belt-and-suspenders guard
in galleryThumbs, with the defensive test extended to cover the case.

Also adds a parallel hidden-embed test for gallery alongside the
existing images variant. Functionally redundant with the early-return
at the top of extractPostMedia, but asserts the contract directly
against the gallery branch so it survives any future refactor that
moves the hide check into a per-shape branch.
This commit is contained in:
Michael Black
2026-06-09 10:10:32 -05:00
parent a88cfcd8fa
commit 2c04d28a9b
2 changed files with 29 additions and 4 deletions
+5 -1
View File
@@ -183,7 +183,8 @@ func imageThumbs(images []*appbsky.EmbedImages_ViewImage) []string {
// galleryThumbs returns the thumbnail URLs of image items in a gallery
// embed, or nil if empty. Items_Elem is a union; non-image variants and
// nil entries are skipped so future gallery item types don't break SEO
// extraction.
// extraction. Empty Thumbnail strings are also skipped to avoid emitting
// <meta property="og:image" content=""> if the appview ever returns one.
func galleryThumbs(items []*appbsky.EmbedGallery_View_Items_Elem) []string {
if len(items) == 0 {
return nil
@@ -193,6 +194,9 @@ func galleryThumbs(items []*appbsky.EmbedGallery_View_Items_Elem) []string {
if item == nil || item.EmbedGallery_ViewImage == nil {
continue
}
if item.EmbedGallery_ViewImage.Thumbnail == "" {
continue
}
urls = append(urls, item.EmbedGallery_ViewImage.Thumbnail)
}
if len(urls) == 0 {
+24 -3
View File
@@ -402,9 +402,10 @@ func TestBuildPostJSONLD_GalleryInRecordWithMedia(t *testing.T) {
}
}
// Forward-compat: nil items and unknown-variant union elements must be
// skipped, not panic. Unknown variants are dropped silently so older
// deploys keep working when new gallery item types ship.
// Forward-compat: nil items, unknown-variant union elements, and empty
// Thumbnail strings must be skipped, not panic or leak as <meta
// property="og:image" content="">. Unknown variants are dropped silently
// so older deploys keep working when new gallery item types ship.
func TestExtractPostMedia_GallerySkipsUnknownItems(t *testing.T) {
thumb := "https://cdn.bsky.app/img/feed_thumbnail/plain/did:plc:alice/g@jpeg"
pv := makePostView("alice.bsky.social", "did:plc:alice", "abc123", "gallery")
@@ -413,6 +414,7 @@ func TestExtractPostMedia_GallerySkipsUnknownItems(t *testing.T) {
Items: []*appbsky.EmbedGallery_View_Items_Elem{
nil,
{}, // empty union, no variant set
{EmbedGallery_ViewImage: &appbsky.EmbedGallery_ViewImage{Thumbnail: ""}}, // empty Thumbnail
{EmbedGallery_ViewImage: &appbsky.EmbedGallery_ViewImage{Thumbnail: thumb}},
},
},
@@ -495,6 +497,25 @@ func TestBuildPostJSONLD_HiddenEmbed(t *testing.T) {
}
}
// Symmetric guard for the gallery extraction path. Functionally redundant
// with the early-return at the top of extractPostMedia, but exists so the
// hide-embed contract is asserted directly against the gallery branch -
// catches anyone who later moves the embedHidden check inside an
// embed-shape branch.
func TestBuildPostJSONLD_HiddenEmbed_Gallery(t *testing.T) {
thumb := "https://cdn.bsky.app/img/g@jpeg"
pv := makePostView("alice.bsky.social", "did:plc:alice", "abc123", "nsfw",
withGallery(thumb), withSelfLabel("porn"))
out, _ := buildPostJSONLD(pv, nil, "u", hideEmbedLabels, hideReplyLabels)
main := unmarshalLD(t, out)["mainEntity"].(map[string]any)
if _, present := main["image"]; present {
t.Errorf("hidden-embed gallery post should not emit image")
}
if _, present := main["thumbnailUrl"]; present {
t.Errorf("hidden-embed gallery post should not emit thumbnailUrl")
}
}
func TestBuildPostJSONLD_TextEscaping(t *testing.T) {
// Includes ", \, newline, </script>, and a unicode char.
tricky := "hello \"world\" \\ <\\>\n</script> 🎉"