From c01e230b2a903d30651448bb3e803ba5413615d5 Mon Sep 17 00:00:00 2001 From: Chris Bongers Date: Fri, 24 Jan 2025 14:06:55 +0200 Subject: [PATCH 1/5] fix: shared post empty state --- __tests__/workers/postBannedRep.ts | 23 +++++++++++- .../workers/postDeletedSharedPostCleanup.ts | 35 ++++++++++++++++++- src/common/utils.ts | 6 ++++ src/migration/1737718327973-DeletedPost.ts | 21 +++++++++++ src/schema/posts.ts | 2 +- src/workers/cdc/primary.ts | 2 ++ src/workers/postBannedRep.ts | 5 +++ src/workers/postDeletedSharedPostCleanup.ts | 35 ++++++++++++++++++- 8 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 src/migration/1737718327973-DeletedPost.ts diff --git a/__tests__/workers/postBannedRep.ts b/__tests__/workers/postBannedRep.ts index 6d7ec351b..779dbe726 100644 --- a/__tests__/workers/postBannedRep.ts +++ b/__tests__/workers/postBannedRep.ts @@ -15,7 +15,11 @@ import { postsFixture } from '../fixture/post'; import { PostReport, ReputationEvent } from '../../src/entity'; import { DataSource, LessThan } from 'typeorm'; import createOrGetConnection from '../../src/db'; -import { createSquadWelcomePost, updateFlagsStatement } from '../../src/common'; +import { + createSquadWelcomePost, + DELETED_BY_WORKER, + updateFlagsStatement, +} from '../../src/common'; import { ReportReason } from '../../src/entity/common'; let con: DataSource; @@ -61,6 +65,23 @@ it('should create a reputation event that increases reputation', async () => { expect(events[1].amount).toEqual(100); }); +it('should not create a reputation event if deleted by worker', async () => { + const post = await con.getRepository(Post).findOneBy({ id: 'p1' }); + await expectSuccessfulBackground(worker, { + post: { + ...post, + flags: { + ...post.flags, + deletedBy: DELETED_BY_WORKER, + }, + }, + }); + const events = await con + .getRepository(ReputationEvent) + .find({ where: { targetId: 'p1', grantById: '' } }); + expect(events.length).toEqual(0); +}); + const createSharedPost = async (id = 'sp1', args: Partial = {}) => { const post = await con.getRepository(Post).findOneBy({ id: 'p1' }); await con.getRepository(SharePost).save({ diff --git a/__tests__/workers/postDeletedSharedPostCleanup.ts b/__tests__/workers/postDeletedSharedPostCleanup.ts index 75f211ed3..7ec020d1d 100644 --- a/__tests__/workers/postDeletedSharedPostCleanup.ts +++ b/__tests__/workers/postDeletedSharedPostCleanup.ts @@ -6,6 +6,7 @@ import { Post, SharePost, Source } from '../../src/entity'; import { postsFixture, sharedPostsFixture } from '../fixture/post'; import { sourcesFixture } from '../fixture'; import { workers } from '../../src/workers'; +import { DELETED_BY_WORKER } from '../../src/common'; let con: DataSource; beforeEach(async () => { @@ -34,6 +35,20 @@ beforeEach(async () => { }; }), ); + await saveFixtures( + con, + SharePost, + sharedPostsFixture.map((item) => { + return { + ...item, + id: `pdspc-nc-${item.id}`, + shortId: `pdspcns1`, + sharedPostId: `pdspc-p2`, + title: null, + titleHtml: null, + }; + }), + ); }); describe('postDeletedSharedPostCleanup worker', () => { @@ -49,7 +64,23 @@ describe('postDeletedSharedPostCleanup worker', () => { expect(registeredWorker).toBeDefined(); }); - it('should set shared post to not show on feed if post gets deleted', async () => { + it('should set shared post with no commentary to not show on feed and be shadow banned if post gets deleted', async () => { + await expectSuccessfulBackground(worker, { + post: { + id: 'pdspc-p2', + }, + }); + const sharedPost = await con.getRepository(SharePost).findOneBy({ + id: 'pdspc-nc-squadP1', + }); + expect(sharedPost?.sharedPostId).toBe('404'); + expect(sharedPost?.deleted).toBe(true); + expect(sharedPost?.flags?.deletedBy).toBe(DELETED_BY_WORKER); + expect(sharedPost?.showOnFeed).toBe(false); + expect(sharedPost?.flags?.showOnFeed).toEqual(false); + }); + + it('should set shared post with commentary to not show on feed if post gets deleted', async () => { await expectSuccessfulBackground(worker, { post: { id: 'pdspc-p1', @@ -58,6 +89,8 @@ describe('postDeletedSharedPostCleanup worker', () => { const sharedPost = await con.getRepository(SharePost).findOneBy({ id: 'pdspc-squadP1', }); + expect(sharedPost?.sharedPostId).toBe('404'); + expect(sharedPost?.deleted).toBe(false); expect(sharedPost?.showOnFeed).toBe(false); expect(sharedPost?.flags?.showOnFeed).toEqual(false); }); diff --git a/src/common/utils.ts b/src/common/utils.ts index 405182bf8..2a470064a 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -7,12 +7,18 @@ import { ChangeObject } from '../types'; const REMOVE_SPECIAL_CHARACTERS_REGEX = /[^a-zA-Z0-9-_#.]/g; +export const DELETED_BY_WORKER = 'worker'; + export const ghostUser = { id: '404', username: 'ghost', name: 'Deleted user', }; +export const deletedPost = { + id: '404', +}; + interface GetTimezonedIsoWeekProps { date: Date; timezone: string; diff --git a/src/migration/1737718327973-DeletedPost.ts b/src/migration/1737718327973-DeletedPost.ts new file mode 100644 index 000000000..2bcc4ece1 --- /dev/null +++ b/src/migration/1737718327973-DeletedPost.ts @@ -0,0 +1,21 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DeletedPost1737718327973 implements MigrationInterface { + name = 'DeletedPost1737718327973'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `INSERT INTO "public"."post" ("id", "sourceId", "deleted", "shortId", "showOnFeed", "flags") VALUES ('404', 'unknown', true, '404', false, '{"visible": true, "showOnFeed": false, "sentAnalyticsReport": true}');`, + ); + await queryRunner.query( + `CREATE RULE prototect_ghostpost_deletion AS ON DELETE TO "post" WHERE old.id IN ('404') DO INSTEAD nothing;`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DELETE FROM "public"."user" where id = "404"`); + await queryRunner.query( + `DROP RULE prototect_ghostpost_deletion on "post";`, + ); + } +} diff --git a/src/schema/posts.ts b/src/schema/posts.ts index 5e6b5f417..59b257ce7 100644 --- a/src/schema/posts.ts +++ b/src/schema/posts.ts @@ -2347,7 +2347,7 @@ export const resolvers: IResolvers = traceResolvers< await Promise.all([ ctx.con.getRepository(Post).findOneByOrFail({ id }), ensureSourcePermissions(ctx, sourceId, SourcePermissions.Post), - ensurePostRateLimit(ctx.con, ctx.userId), + // ensurePostRateLimit(ctx.con, ctx.userId), ]); const newPost = await createSharePost({ diff --git a/src/workers/cdc/primary.ts b/src/workers/cdc/primary.ts index 105dfd53a..d8516e4b7 100644 --- a/src/workers/cdc/primary.ts +++ b/src/workers/cdc/primary.ts @@ -588,6 +588,8 @@ const onPostChange = async ( { metadataChangedAt: new Date() }, ); } + } else if (data.payload.op === 'd') { + await notifyPostBannedOrRemoved(logger, data.payload.before!); } }; diff --git a/src/workers/postBannedRep.ts b/src/workers/postBannedRep.ts index 8ac1c69b3..92cb7c466 100644 --- a/src/workers/postBannedRep.ts +++ b/src/workers/postBannedRep.ts @@ -7,6 +7,7 @@ import { messageToJson, Worker } from './worker'; import { PostReport } from '../entity/PostReport'; import { Post } from '../entity'; import { ChangeObject } from '../types'; +import { DELETED_BY_WORKER } from '../common'; interface Data { post: ChangeObject; @@ -21,6 +22,10 @@ const worker: Worker = { typeof flags === 'string' ? JSON.parse(flags as string) : flags; const { deletedBy } = parsedFlags; + if (deletedBy === DELETED_BY_WORKER) { + return; + } + try { await con.transaction(async (transaction) => { const reports = await transaction diff --git a/src/workers/postDeletedSharedPostCleanup.ts b/src/workers/postDeletedSharedPostCleanup.ts index 12ca59d5a..69694c299 100644 --- a/src/workers/postDeletedSharedPostCleanup.ts +++ b/src/workers/postDeletedSharedPostCleanup.ts @@ -1,12 +1,24 @@ import { messageToJson, Worker } from './worker'; import { Post, SharePost } from '../entity/'; import { ChangeObject } from '../types'; -import { updateFlagsStatement } from '../common'; +import { + DELETED_BY_WORKER, + deletedPost, + updateFlagsStatement, +} from '../common'; +import { Not, IsNull } from 'typeorm'; interface Data { post: ChangeObject; } +/** + * This worker is responsible for managing shared post state when referenced post is deleted. + * Rules: + * - When a post is deleted, all shared posts referencing it should be set to link to DELETED_POST + * - Shared posts with DELETED_POST should not show on feed + * - Shared posts with DELETED_POST and no commentary should be soft deleted as well but not decrease reputation + */ const worker: Worker = { subscription: 'api.post-deleted-shared-post-cleanup', handler: async (message, con, logger): Promise => { @@ -32,9 +44,30 @@ const worker: Worker = { .update() .where({ sharedPostId: post.id, + title: IsNull(), }) .set({ + deleted: true, showOnFeed: false, + sharedPostId: deletedPost.id, + flags: updateFlagsStatement({ + showOnFeed: false, + deletedBy: DELETED_BY_WORKER, + }), + }) + .execute(); + + await con + .getRepository(SharePost) + .createQueryBuilder() + .update() + .where({ + sharedPostId: post.id, + title: Not(IsNull()), + }) + .set({ + showOnFeed: false, + sharedPostId: deletedPost.id, flags: updateFlagsStatement({ showOnFeed: false, }), From 58be4ee458b1158e4652b1d602142b7f819b4ace Mon Sep 17 00:00:00 2001 From: Chris Bongers Date: Fri, 24 Jan 2025 14:26:33 +0200 Subject: [PATCH 2/5] Update src/schema/posts.ts --- src/schema/posts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/schema/posts.ts b/src/schema/posts.ts index 59b257ce7..5e6b5f417 100644 --- a/src/schema/posts.ts +++ b/src/schema/posts.ts @@ -2347,7 +2347,7 @@ export const resolvers: IResolvers = traceResolvers< await Promise.all([ ctx.con.getRepository(Post).findOneByOrFail({ id }), ensureSourcePermissions(ctx, sourceId, SourcePermissions.Post), - // ensurePostRateLimit(ctx.con, ctx.userId), + ensurePostRateLimit(ctx.con, ctx.userId), ]); const newPost = await createSharePost({ From 9c7194d5e595f7f095bbc8ce1a78a2faa8346c20 Mon Sep 17 00:00:00 2001 From: Chris Bongers Date: Fri, 24 Jan 2025 14:53:00 +0200 Subject: [PATCH 3/5] fix: tests and PR feedback --- __tests__/cron/checkAnalyticsReport.ts | 2 +- __tests__/workers/postUpdated.ts | 32 +++++----- src/migration/1737718327973-DeletedPost.ts | 2 +- src/workers/postDeletedSharedPostCleanup.ts | 67 +++++++++++---------- 4 files changed, 52 insertions(+), 51 deletions(-) diff --git a/__tests__/cron/checkAnalyticsReport.ts b/__tests__/cron/checkAnalyticsReport.ts index 5bf59dcf3..7de6a70e2 100644 --- a/__tests__/cron/checkAnalyticsReport.ts +++ b/__tests__/cron/checkAnalyticsReport.ts @@ -78,7 +78,7 @@ it('should publish message for every post that needs analytics report', async () const posts = await con .getRepository(Post) .findBy({ sentAnalyticsReport: true }); - expect(posts.length).toEqual(4); + expect(posts.length).toEqual(5); expect(posts.map(({ id }) => id)).toEqual( expect.arrayContaining(['p3', 'p4', 'p5']), ); diff --git a/__tests__/workers/postUpdated.ts b/__tests__/workers/postUpdated.ts index 0ed630b94..a6e26072f 100644 --- a/__tests__/workers/postUpdated.ts +++ b/__tests__/workers/postUpdated.ts @@ -445,8 +445,8 @@ it('should save a new post with basic information', async () => { order: 0, }); const posts = await con.getRepository(Post).find(); - expect(posts.length).toEqual(3); - expect(posts[2]).toMatchSnapshot({ + expect(posts.length).toEqual(4); + expect(posts[3]).toMatchSnapshot({ visible: true, visibleAt: expect.any(Date), createdAt: expect.any(Date), @@ -476,8 +476,8 @@ it('should save a new post with with non-default language', async () => { language: 'nb', }); const posts = await con.getRepository(Post).find(); - expect(posts.length).toEqual(3); - expect(posts[2]).toMatchObject({ + expect(posts.length).toEqual(4); + expect(posts[3]).toMatchObject({ sourceId: 'a', title: 'Title', showOnFeed: true, @@ -493,7 +493,7 @@ it('should set show on feed to true when order is missing', async () => { source_id: 'a', }); const posts = await con.getRepository(Post).find(); - expect(posts.length).toEqual(3); + expect(posts.length).toEqual(4); expect(posts[2].showOnFeed).toEqual(true); }); @@ -506,8 +506,8 @@ it('should save a new post with showOnFeed information', async () => { order: 1, }); const posts = await con.getRepository(Post).find(); - expect(posts.length).toEqual(3); - expect(posts[2].showOnFeed).toEqual(false); + expect(posts.length).toEqual(4); + expect(posts[3].showOnFeed).toEqual(false); }); it('should save a new post with content curation', async () => { @@ -521,7 +521,7 @@ it('should save a new post with content curation', async () => { }, }); const posts = await con.getRepository(Post).find(); - expect(posts.length).toEqual(3); + expect(posts.length).toEqual(4); const post = await con .getRepository(Post) .findOneBy({ yggdrasilId: 'f99a445f-e2fb-48e8-959c-e02a17f5e816' }); @@ -540,9 +540,9 @@ it('save a post as public if source is public', async () => { source_id: 'a', }); const posts = await con.getRepository(Post).find(); - expect(posts.length).toEqual(3); + expect(posts.length).toEqual(4); expect(posts[2].private).toEqual(false); - expect(posts[2].flags.private).toEqual(false); + expect(posts[3].flags.private).toEqual(false); }); it('save a post as private if source is private', async () => { @@ -553,9 +553,9 @@ it('save a post as private if source is private', async () => { source_id: 'p', }); const posts = await con.getRepository(Post).find(); - expect(posts.length).toEqual(3); - expect(posts[2].private).toBe(true); - expect(posts[2].flags.private).toBe(true); + expect(posts.length).toEqual(4); + expect(posts[3].private).toBe(true); + expect(posts[3].flags.private).toBe(true); }); it('do not save post if source can not be found', async () => { @@ -663,9 +663,9 @@ it('should save a new post with the relevant keywords', async () => { }, }); const posts = await con.getRepository(Post).find(); - expect(posts.length).toEqual(3); - expect(posts[2].scoutId).toEqual('1'); - const tagsArray = posts[2].tagsStr.split(','); + expect(posts.length).toEqual(4); + expect(posts[3].scoutId).toEqual('1'); + const tagsArray = posts[3].tagsStr.split(','); ['mongodb', 'alpinejs', 'ab-testing'].forEach((item) => { expect(tagsArray).toContain(item); }); diff --git a/src/migration/1737718327973-DeletedPost.ts b/src/migration/1737718327973-DeletedPost.ts index 2bcc4ece1..2de45f5d6 100644 --- a/src/migration/1737718327973-DeletedPost.ts +++ b/src/migration/1737718327973-DeletedPost.ts @@ -5,7 +5,7 @@ export class DeletedPost1737718327973 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { await queryRunner.query( - `INSERT INTO "public"."post" ("id", "sourceId", "deleted", "shortId", "showOnFeed", "flags") VALUES ('404', 'unknown', true, '404', false, '{"visible": true, "showOnFeed": false, "sentAnalyticsReport": true}');`, + `INSERT INTO "public"."post" ("id", "sourceId", "deleted", "shortId", "showOnFeed", "flags") VALUES ('404', 'unknown', true, '404', false, '{"visible": true, "showOnFeed": false, "sentAnalyticsReport": false}');`, ); await queryRunner.query( `CREATE RULE prototect_ghostpost_deletion AS ON DELETE TO "post" WHERE old.id IN ('404') DO INSTEAD nothing;`, diff --git a/src/workers/postDeletedSharedPostCleanup.ts b/src/workers/postDeletedSharedPostCleanup.ts index 69694c299..899bf015c 100644 --- a/src/workers/postDeletedSharedPostCleanup.ts +++ b/src/workers/postDeletedSharedPostCleanup.ts @@ -38,41 +38,42 @@ const worker: Worker = { ); } - await con - .getRepository(SharePost) - .createQueryBuilder() - .update() - .where({ - sharedPostId: post.id, - title: IsNull(), - }) - .set({ - deleted: true, - showOnFeed: false, - sharedPostId: deletedPost.id, - flags: updateFlagsStatement({ + await Promise.all([ + await con + .getRepository(SharePost) + .createQueryBuilder() + .update() + .where({ + sharedPostId: post.id, + title: IsNull(), + }) + .set({ + deleted: true, showOnFeed: false, - deletedBy: DELETED_BY_WORKER, - }), - }) - .execute(); - - await con - .getRepository(SharePost) - .createQueryBuilder() - .update() - .where({ - sharedPostId: post.id, - title: Not(IsNull()), - }) - .set({ - showOnFeed: false, - sharedPostId: deletedPost.id, - flags: updateFlagsStatement({ + sharedPostId: deletedPost.id, + flags: updateFlagsStatement({ + showOnFeed: false, + deletedBy: DELETED_BY_WORKER, + }), + }) + .execute(), + await con + .getRepository(SharePost) + .createQueryBuilder() + .update() + .where({ + sharedPostId: post.id, + title: Not(IsNull()), + }) + .set({ showOnFeed: false, - }), - }) - .execute(); + sharedPostId: deletedPost.id, + flags: updateFlagsStatement({ + showOnFeed: false, + }), + }) + .execute(), + ]); logger.info( { From 006104722b7469e9df5bf49a278c48eeb7901e66 Mon Sep 17 00:00:00 2001 From: Chris Bongers Date: Fri, 24 Jan 2025 15:10:23 +0200 Subject: [PATCH 4/5] fix: more tests --- __tests__/workers/cdc/primary.ts | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/__tests__/workers/cdc/primary.ts b/__tests__/workers/cdc/primary.ts index 4f1d1ac10..a594ed0ae 100644 --- a/__tests__/workers/cdc/primary.ts +++ b/__tests__/workers/cdc/primary.ts @@ -115,7 +115,7 @@ import { relatedPostsFixture, } from '../../fixture/post'; import { randomUUID } from 'crypto'; -import { DataSource } from 'typeorm'; +import { DataSource, Not } from 'typeorm'; import createOrGetConnection from '../../../src/db'; import { TypeOrmError } from '../../../src/errors'; import { SourceMemberRoles } from '../../../src/roles'; @@ -4855,7 +4855,7 @@ describe('source_post_moderation', () => { it('should create freeform post', async () => { const repo = con.getRepository(Post); const before = await repo.find(); - expect(before.length).toEqual(0); + expect(before.length).toEqual(1); const after = { ...base, status: SourcePostModerationStatus.Approved, @@ -4886,7 +4886,7 @@ describe('source_post_moderation', () => { await saveFixtures(con, User, badUsersFixture); const repo = con.getRepository(Post); const before = await repo.find(); - expect(before.length).toEqual(0); + expect(before.length).toEqual(1); const after = { ...base, createdById: 'vordr', @@ -4935,7 +4935,7 @@ describe('source_post_moderation', () => { ]); const repo = con.getRepository(Post); const before = await repo.find(); - expect(before.length).toEqual(0); + expect(before.length).toEqual(1); const after = { ...base, status: SourcePostModerationStatus.Approved, @@ -4959,7 +4959,7 @@ describe('source_post_moderation', () => { it('should not create post if status did not change', async () => { const repo = con.getRepository(Post); const before = await repo.find(); - expect(before.length).toEqual(0); + expect(before.length).toEqual(1); const after = { ...base, status: SourcePostModerationStatus.Approved, @@ -4979,7 +4979,7 @@ describe('source_post_moderation', () => { it('should not create share post when share post id is null', async () => { const repo = con.getRepository(Post); const before = await repo.find(); - expect(before.length).toEqual(0); + expect(before.length).toEqual(1); await mockUpdate({ type: PostType.Share, status: SourcePostModerationStatus.Approved, @@ -5054,7 +5054,7 @@ describe('source_post_moderation', () => { .update({ id: 'b' }, { id: UNKNOWN_SOURCE }); const repo = con.getRepository(Post); const before = await repo.find(); - expect(before.length).toEqual(0); + expect(before.length).toEqual(1); await mockUpdate({ sourceId: 'a', type: PostType.Share, @@ -5063,8 +5063,8 @@ describe('source_post_moderation', () => { content: '# Sample', contentHtml: '# Sample', }); - const unknown = await repo.findOneBy({ sourceId: UNKNOWN_SOURCE }); - expect(unknown).toBeNull(); + const unknown = await repo.findBy({ sourceId: UNKNOWN_SOURCE }); + expect(unknown.length).toEqual(1); const share = await repo.findOneBy({ sourceId: 'a' }); expect(share).toBeNull(); }); @@ -5075,7 +5075,7 @@ describe('source_post_moderation', () => { .update({ id: 'b' }, { id: UNKNOWN_SOURCE }); const repo = con.getRepository(Post); const before = await repo.find(); - expect(before.length).toEqual(0); + expect(before.length).toEqual(1); const after = { ...base, sourceId: 'a', @@ -5087,7 +5087,10 @@ describe('source_post_moderation', () => { externalLink: 'https://daily.dev/blog-post/sauron', }; await mockUpdate(after); - const unknown = await repo.findOneBy({ sourceId: UNKNOWN_SOURCE }); + const unknown = await repo.findOneBy({ + sourceId: UNKNOWN_SOURCE, + id: Not('404'), + }); expect(unknown).toBeTruthy(); expect(unknown.title).toEqual('Test'); const share = (await repo.findOneBy({ @@ -5116,7 +5119,7 @@ describe('source_post_moderation', () => { }, ]); const before = await repo.find(); - expect(before.length).toEqual(1); + expect(before.length).toEqual(2); const after = { ...base, sourceId: 'a', @@ -5141,7 +5144,7 @@ describe('source_post_moderation', () => { ]); const list = await con.getRepository(Post).find(); - expect(list.length).toEqual(2); // to ensure nothing new was created other than the share post + expect(list.length).toEqual(3); // to ensure nothing new was created other than the share post }); it('should update the content if post id is present', async () => { From 1df4d8fd0b79065756cc07576b801b793befc09b Mon Sep 17 00:00:00 2001 From: Chris Bongers Date: Fri, 24 Jan 2025 15:21:51 +0200 Subject: [PATCH 5/5] fix: more tests --- __tests__/cron/updateTagsStr.ts | 3 ++- __tests__/cron/updateTrending.ts | 10 ++++++---- __tests__/cron/updateViews.ts | 3 ++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/__tests__/cron/updateTagsStr.ts b/__tests__/cron/updateTagsStr.ts index 3e131c1b8..02a205f90 100644 --- a/__tests__/cron/updateTagsStr.ts +++ b/__tests__/cron/updateTagsStr.ts @@ -10,7 +10,7 @@ import { import { sourcesFixture } from '../fixture/source'; import { postsFixture, sharedPostsFixture } from '../fixture/post'; import { Checkpoint } from '../../src/entity/Checkpoint'; -import { DataSource } from 'typeorm'; +import { DataSource, Not } from 'typeorm'; import createOrGetConnection from '../../src/db'; let con: DataSource; @@ -57,6 +57,7 @@ it('should update post tagsStr with the all recently updated keywords', async () const posts = await con.getRepository(Post).find({ select: ['id', 'tagsStr'], order: { id: 'ASC' }, + where: { id: Not('404') }, }); expect(posts).toMatchSnapshot(); }); diff --git a/__tests__/cron/updateTrending.ts b/__tests__/cron/updateTrending.ts index 4baec287b..0f79c9f87 100644 --- a/__tests__/cron/updateTrending.ts +++ b/__tests__/cron/updateTrending.ts @@ -5,7 +5,7 @@ import { sourcesFixture } from '../fixture/source'; import { postsFixture, sharedPostsFixture } from '../fixture/post'; import { DeepPartial } from 'typeorm/common/DeepPartial'; -import { DataSource } from 'typeorm'; +import { DataSource, Not } from 'typeorm'; import createOrGetConnection from '../../src/db'; let con: DataSource; @@ -51,9 +51,11 @@ it('should update the trending score of the relevant articles', async () => { addViewsToPost(postsFixture[2].id, 30, halfHour), ]); await expectSuccessfulCron(cron); - const posts = await con - .getRepository(Post) - .find({ select: ['id', 'trending'], order: { id: 'ASC' } }); + const posts = await con.getRepository(Post).find({ + select: ['id', 'trending'], + order: { id: 'ASC' }, + where: { id: Not('404') }, + }); expect(posts).toMatchSnapshot(); const trendingPost = await con.getRepository(Post).findOne({ select: ['id', 'lastTrending'], diff --git a/__tests__/cron/updateViews.ts b/__tests__/cron/updateViews.ts index 814befb61..dd9a3d61f 100644 --- a/__tests__/cron/updateViews.ts +++ b/__tests__/cron/updateViews.ts @@ -2,7 +2,7 @@ import cron from '../../src/cron/updateViews'; import { expectSuccessfulCron, saveFixtures } from '../helpers'; import { ArticlePost, Post, Source, View } from '../../src/entity'; import { sourcesFixture } from '../fixture/source'; -import { DataSource } from 'typeorm'; +import { DataSource, Not } from 'typeorm'; import createOrGetConnection from '../../src/db'; let con: DataSource; @@ -62,6 +62,7 @@ it('should update views', async () => { const posts = await con.getRepository(Post).find({ select: ['id', 'views', 'score', 'createdAt'], order: { createdAt: 'ASC' }, + where: { id: Not('404') }, }); expect(posts[0].views).toEqual(0); expect(posts[1].views).toEqual(2);