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:
@@ -183,7 +183,8 @@ func imageThumbs(images []*appbsky.EmbedImages_ViewImage) []string {
|
|||||||
// galleryThumbs returns the thumbnail URLs of image items in a gallery
|
// 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
|
// 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
|
// 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 {
|
func galleryThumbs(items []*appbsky.EmbedGallery_View_Items_Elem) []string {
|
||||||
if len(items) == 0 {
|
if len(items) == 0 {
|
||||||
return nil
|
return nil
|
||||||
@@ -193,6 +194,9 @@ func galleryThumbs(items []*appbsky.EmbedGallery_View_Items_Elem) []string {
|
|||||||
if item == nil || item.EmbedGallery_ViewImage == nil {
|
if item == nil || item.EmbedGallery_ViewImage == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if item.EmbedGallery_ViewImage.Thumbnail == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
urls = append(urls, item.EmbedGallery_ViewImage.Thumbnail)
|
urls = append(urls, item.EmbedGallery_ViewImage.Thumbnail)
|
||||||
}
|
}
|
||||||
if len(urls) == 0 {
|
if len(urls) == 0 {
|
||||||
|
|||||||
@@ -402,9 +402,10 @@ func TestBuildPostJSONLD_GalleryInRecordWithMedia(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward-compat: nil items and unknown-variant union elements must be
|
// Forward-compat: nil items, unknown-variant union elements, and empty
|
||||||
// skipped, not panic. Unknown variants are dropped silently so older
|
// Thumbnail strings must be skipped, not panic or leak as <meta
|
||||||
// deploys keep working when new gallery item types ship.
|
// 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) {
|
func TestExtractPostMedia_GallerySkipsUnknownItems(t *testing.T) {
|
||||||
thumb := "https://cdn.bsky.app/img/feed_thumbnail/plain/did:plc:alice/g@jpeg"
|
thumb := "https://cdn.bsky.app/img/feed_thumbnail/plain/did:plc:alice/g@jpeg"
|
||||||
pv := makePostView("alice.bsky.social", "did:plc:alice", "abc123", "gallery")
|
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{
|
Items: []*appbsky.EmbedGallery_View_Items_Elem{
|
||||||
nil,
|
nil,
|
||||||
{}, // empty union, no variant set
|
{}, // empty union, no variant set
|
||||||
|
{EmbedGallery_ViewImage: &appbsky.EmbedGallery_ViewImage{Thumbnail: ""}}, // empty Thumbnail
|
||||||
{EmbedGallery_ViewImage: &appbsky.EmbedGallery_ViewImage{Thumbnail: thumb}},
|
{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) {
|
func TestBuildPostJSONLD_TextEscaping(t *testing.T) {
|
||||||
// Includes ", \, newline, </script>, and a unicode char.
|
// Includes ", \, newline, </script>, and a unicode char.
|
||||||
tricky := "hello \"world\" \\ <\\>\n</script> 🎉"
|
tricky := "hello \"world\" \\ <\\>\n</script> 🎉"
|
||||||
|
|||||||
Reference in New Issue
Block a user