diff --git a/__tests__/highlights.ts b/__tests__/highlights.ts index 2e5c9aa23d..2db6f0a8d5 100644 --- a/__tests__/highlights.ts +++ b/__tests__/highlights.ts @@ -1156,6 +1156,44 @@ describe('query postHighlightsFeed', () => { ]); }); + it('should exclude highlights whose post is deleted or not visible', async () => { + await createTestPosts(); + await con.getRepository(ArticlePost).update('h2', { deleted: true }); + await con.getRepository(ArticlePost).update('h3', { visible: false }); + await saveCanonicalHighlights([ + { + postId: 'h1', + channel: 'vibes', + highlightedAt: new Date('2026-03-19T10:40:00.000Z'), + headline: 'Visible headline', + significance: HighlightSignificance.Breaking, + }, + { + postId: 'h2', + channel: 'vibes', + highlightedAt: new Date('2026-03-19T10:35:00.000Z'), + headline: 'Deleted post headline', + significance: HighlightSignificance.Notable, + }, + { + postId: 'h3', + channel: 'agentic', + highlightedAt: new Date('2026-03-19T10:30:00.000Z'), + headline: 'Invisible post headline', + significance: HighlightSignificance.Routine, + }, + ]); + + const res = await client.query(POST_HIGHLIGHTS_FEED_QUERY, { + variables: { first: 10 }, + }); + + expect(res.errors).toBeFalsy(); + expect( + res.data.postHighlightsFeed.edges.map(({ node }) => node.post.id), + ).toEqual(['h1']); + }); + it('should filter by channel', async () => { await createTestPosts(); await saveCanonicalHighlights([ diff --git a/src/schema/highlights.ts b/src/schema/highlights.ts index 741160b7eb..1260ce2be2 100644 --- a/src/schema/highlights.ts +++ b/src/schema/highlights.ts @@ -171,6 +171,20 @@ type HighlightsFilters = { significances?: HighlightSignificance[]; }; +const applyVisiblePostFilter = ( + builder: SelectQueryBuilder, + alias: string, +): SelectQueryBuilder => + builder.andWhere( + `EXISTS ( + SELECT 1 + FROM "post" p + WHERE p."id" = "${alias}"."postId" + AND p."deleted" = false + AND p."visible" = true + )`, + ); + const applyHighlightsFilters = ( builder: SelectQueryBuilder, alias: string, @@ -188,7 +202,7 @@ const applyHighlightsFilters = ( builder.andWhere(`:highlightChannel = ANY("${alias}"."channels")`); } - return builder; + return applyVisiblePostFilter(builder, alias); }; const resolveCanonicalHighlightsFeed = ( @@ -276,6 +290,10 @@ export const resolvers: IResolvers = { }) .orderBy(`"${builder.alias}"."highlightedAt"`, 'DESC') .addOrderBy(`"${builder.alias}"."id"`, 'DESC'); + applyVisiblePostFilter( + builder.queryBuilder as SelectQueryBuilder, + builder.alias, + ); return builder; }, true,