@@ -4,8 +4,10 @@ import { cleanDatabase } from "../tests/helpers";
44import { createAccount } from "../tests/helpers/oauth" ;
55import db from "./db" ;
66import {
7+ createMentionNotifications ,
78 createQuotedUpdateNotifications ,
89 createQuoteNotification ,
10+ createReplyMentionNotification ,
911} from "./notification" ;
1012import * as Schema from "./schema" ;
1113import type { Uuid } from "./uuid" ;
@@ -50,7 +52,10 @@ async function createRemoteAccount(username: string): Promise<Schema.Account> {
5052async function createPost (
5153 accountId : Uuid ,
5254 content : string ,
53- quoteTargetId ?: Uuid ,
55+ options : {
56+ quoteTargetId ?: Uuid ;
57+ replyTargetId ?: Uuid ;
58+ } = { } ,
5459) : Promise < Schema . Post > {
5560 const postId = crypto . randomUUID ( ) as Uuid ;
5661 const postIri = `https://test.example/@test/${ postId } ` ;
@@ -64,14 +69,107 @@ async function createPost(
6469 accountId,
6570 visibility : "public" ,
6671 content,
67- quoteTargetId,
72+ quoteTargetId : options . quoteTargetId ,
73+ replyTargetId : options . replyTargetId ,
6874 published : new Date ( ) ,
6975 } )
7076 . returning ( ) ;
7177
7278 return post ;
7379}
7480
81+ describe ( "Reply mention notifications" , ( ) => {
82+ let localAccount : Awaited < ReturnType < typeof createAccount > > ;
83+ let remoteAccount : Schema . Account ;
84+
85+ beforeEach ( async ( ) => {
86+ await cleanDatabase ( ) ;
87+ localAccount = await createAccount ( ) ;
88+ remoteAccount = await createRemoteAccount ( "remote_user" ) ;
89+ } ) ;
90+
91+ it ( "creates mention notifications for replies to local posts" , async ( ) => {
92+ expect . assertions ( 5 ) ;
93+
94+ const originalPost = await createPost (
95+ localAccount . id as Uuid ,
96+ "Original post" ,
97+ ) ;
98+ const replyPost = await createPost ( remoteAccount . id , "Reply" , {
99+ replyTargetId : originalPost . id ,
100+ } ) ;
101+
102+ const originalPostWithAccount = await db . query . posts . findFirst ( {
103+ where : eq ( Schema . posts . id , originalPost . id ) ,
104+ with : {
105+ account : { with : { owner : true } } ,
106+ } ,
107+ } ) ;
108+
109+ const notificationId = await createReplyMentionNotification (
110+ remoteAccount ,
111+ replyPost ,
112+ originalPostWithAccount ! ,
113+ ) ;
114+
115+ expect ( notificationId ) . not . toBeNull ( ) ;
116+
117+ const notification = await db . query . notifications . findFirst ( {
118+ where : eq ( Schema . notifications . id , notificationId ! ) ,
119+ } ) ;
120+
121+ expect ( notification ) . not . toBeNull ( ) ;
122+ expect ( notification ?. type ) . toBe ( "mention" ) ;
123+ expect ( notification ?. actorAccountId ) . toBe ( remoteAccount . id ) ;
124+ expect ( notification ?. targetPostId ) . toBe ( replyPost . id ) ;
125+ } ) ;
126+
127+ it ( "does not duplicate reply mention notifications when the reply also mentions the original author" , async ( ) => {
128+ expect . assertions ( 2 ) ;
129+
130+ const originalPost = await createPost (
131+ localAccount . id as Uuid ,
132+ "Original post" ,
133+ ) ;
134+ const replyPost = await createPost ( remoteAccount . id , "Reply" , {
135+ replyTargetId : originalPost . id ,
136+ } ) ;
137+
138+ const originalPostWithAccount = await db . query . posts . findFirst ( {
139+ where : eq ( Schema . posts . id , originalPost . id ) ,
140+ with : {
141+ account : { with : { owner : true } } ,
142+ } ,
143+ } ) ;
144+ const mentionedAccount = await db . query . accounts . findFirst ( {
145+ where : eq ( Schema . accounts . id , localAccount . id as Uuid ) ,
146+ with : { owner : true } ,
147+ } ) ;
148+
149+ await createReplyMentionNotification (
150+ remoteAccount ,
151+ replyPost ,
152+ originalPostWithAccount ! ,
153+ ) ;
154+ const mentionNotificationIds = await createMentionNotifications (
155+ replyPost ,
156+ [ mentionedAccount ! ] ,
157+ originalPost . accountId ,
158+ ) ;
159+
160+ expect ( mentionNotificationIds ) . toHaveLength ( 0 ) ;
161+
162+ const notifications = await db . query . notifications . findMany ( {
163+ where : and (
164+ eq ( Schema . notifications . type , "mention" ) ,
165+ eq ( Schema . notifications . targetPostId , replyPost . id ) ,
166+ ) ,
167+ } ) ;
168+
169+ expect ( notifications ) . toHaveLength ( 1 ) ;
170+ } ) ;
171+ } ) ;
172+
75173describe ( "Quote notifications" , ( ) => {
76174 let localAccount : Awaited < ReturnType < typeof createAccount > > ;
77175 let remoteAccount : Schema . Account ;
@@ -103,11 +201,9 @@ describe("Quote notifications", () => {
103201 expect ( originalPostWithAccount ) . not . toBeNull ( ) ;
104202
105203 // Create quote post by remote user
106- const quotePost = await createPost (
107- remoteAccount . id ,
108- "Quoting this!" ,
109- originalPost . id ,
110- ) ;
204+ const quotePost = await createPost ( remoteAccount . id , "Quoting this!" , {
205+ quoteTargetId : originalPost . id ,
206+ } ) ;
111207
112208 // Create quote notification
113209 const notificationId = await createQuoteNotification (
@@ -156,7 +252,7 @@ describe("Quote notifications", () => {
156252 const quotePost = await createPost (
157253 localAccount . id as Uuid ,
158254 "Quoting my own post" ,
159- originalPost . id ,
255+ { quoteTargetId : originalPost . id } ,
160256 ) ;
161257
162258 // Create quote notification - should return null for self-quote
@@ -187,11 +283,9 @@ describe("Quote notifications", () => {
187283 const anotherRemote = await createRemoteAccount ( "another_remote" ) ;
188284
189285 // Create quote post
190- const quotePost = await createPost (
191- anotherRemote . id ,
192- "Quoting remote" ,
193- originalPost . id ,
194- ) ;
286+ const quotePost = await createPost ( anotherRemote . id , "Quoting remote" , {
287+ quoteTargetId : originalPost . id ,
288+ } ) ;
195289
196290 // Create quote notification - should return null (original author not local)
197291 const notificationId = await createQuoteNotification (
@@ -212,11 +306,9 @@ describe("Quote notifications", () => {
212306 const originalPost = await createPost ( remoteAccount . id , "Original post" ) ;
213307
214308 // Local user creates a quote post
215- const quotePost = await createPost (
216- localAccount . id as Uuid ,
217- "My quote" ,
218- originalPost . id ,
219- ) ;
309+ const quotePost = await createPost ( localAccount . id as Uuid , "My quote" , {
310+ quoteTargetId : originalPost . id ,
311+ } ) ;
220312
221313 // Get local account with owner info
222314 const quoteAuthor = await db . query . accounts . findFirst ( {
@@ -308,11 +400,9 @@ describe("Quote notifications", () => {
308400 } ) ;
309401
310402 // Create quote post by remote user
311- const quotePost = await createPost (
312- remoteAccount . id ,
313- "Quoting this!" ,
314- originalPost . id ,
315- ) ;
403+ const quotePost = await createPost ( remoteAccount . id , "Quoting this!" , {
404+ quoteTargetId : originalPost . id ,
405+ } ) ;
316406
317407 // Create quote notification twice
318408 const notificationId1 = await createQuoteNotification (
0 commit comments