@@ -5,8 +5,10 @@ import { cleanDatabase } from "../tests/helpers";
55import { createAccount } from "../tests/helpers/oauth" ;
66import db from "./db" ;
77import {
8+ createMentionNotifications ,
89 createQuotedUpdateNotifications ,
910 createQuoteNotification ,
11+ createReplyMentionNotification ,
1012} from "./notification" ;
1113import * as Schema from "./schema" ;
1214import type { Uuid } from "./uuid" ;
@@ -51,7 +53,10 @@ async function createRemoteAccount(username: string): Promise<Schema.Account> {
5153async function createPost (
5254 accountId : Uuid ,
5355 content : string ,
54- quoteTargetId ?: Uuid ,
56+ options : {
57+ quoteTargetId ?: Uuid ;
58+ replyTargetId ?: Uuid ;
59+ } = { } ,
5560) : Promise < Schema . Post > {
5661 const postId = crypto . randomUUID ( ) as Uuid ;
5762 const postIri = `https://test.example/@test/${ postId } ` ;
@@ -65,14 +70,107 @@ async function createPost(
6570 accountId,
6671 visibility : "public" ,
6772 content,
68- quoteTargetId,
73+ quoteTargetId : options . quoteTargetId ,
74+ replyTargetId : options . replyTargetId ,
6975 published : new Date ( ) ,
7076 } )
7177 . returning ( ) ;
7278
7379 return post ;
7480}
7581
82+ describe ( "Reply mention notifications" , ( ) => {
83+ let localAccount : Awaited < ReturnType < typeof createAccount > > ;
84+ let remoteAccount : Schema . Account ;
85+
86+ beforeEach ( async ( ) => {
87+ await cleanDatabase ( ) ;
88+ localAccount = await createAccount ( ) ;
89+ remoteAccount = await createRemoteAccount ( "remote_user" ) ;
90+ } ) ;
91+
92+ it ( "creates mention notifications for replies to local posts" , async ( ) => {
93+ expect . assertions ( 5 ) ;
94+
95+ const originalPost = await createPost (
96+ localAccount . id as Uuid ,
97+ "Original post" ,
98+ ) ;
99+ const replyPost = await createPost ( remoteAccount . id , "Reply" , {
100+ replyTargetId : originalPost . id ,
101+ } ) ;
102+
103+ const originalPostWithAccount = await db . query . posts . findFirst ( {
104+ where : eq ( Schema . posts . id , originalPost . id ) ,
105+ with : {
106+ account : { with : { owner : true } } ,
107+ } ,
108+ } ) ;
109+
110+ const notificationId = await createReplyMentionNotification (
111+ remoteAccount ,
112+ replyPost ,
113+ originalPostWithAccount ! ,
114+ ) ;
115+
116+ expect ( notificationId ) . not . toBeNull ( ) ;
117+
118+ const notification = await db . query . notifications . findFirst ( {
119+ where : eq ( Schema . notifications . id , notificationId ! ) ,
120+ } ) ;
121+
122+ expect ( notification ) . not . toBeNull ( ) ;
123+ expect ( notification ?. type ) . toBe ( "mention" ) ;
124+ expect ( notification ?. actorAccountId ) . toBe ( remoteAccount . id ) ;
125+ expect ( notification ?. targetPostId ) . toBe ( replyPost . id ) ;
126+ } ) ;
127+
128+ it ( "does not duplicate reply mention notifications when the reply also mentions the original author" , async ( ) => {
129+ expect . assertions ( 2 ) ;
130+
131+ const originalPost = await createPost (
132+ localAccount . id as Uuid ,
133+ "Original post" ,
134+ ) ;
135+ const replyPost = await createPost ( remoteAccount . id , "Reply" , {
136+ replyTargetId : originalPost . id ,
137+ } ) ;
138+
139+ const originalPostWithAccount = await db . query . posts . findFirst ( {
140+ where : eq ( Schema . posts . id , originalPost . id ) ,
141+ with : {
142+ account : { with : { owner : true } } ,
143+ } ,
144+ } ) ;
145+ const mentionedAccount = await db . query . accounts . findFirst ( {
146+ where : eq ( Schema . accounts . id , localAccount . id as Uuid ) ,
147+ with : { owner : true } ,
148+ } ) ;
149+
150+ await createReplyMentionNotification (
151+ remoteAccount ,
152+ replyPost ,
153+ originalPostWithAccount ! ,
154+ ) ;
155+ const mentionNotificationIds = await createMentionNotifications (
156+ replyPost ,
157+ [ mentionedAccount ! ] ,
158+ originalPost . accountId ,
159+ ) ;
160+
161+ expect ( mentionNotificationIds ) . toHaveLength ( 0 ) ;
162+
163+ const notifications = await db . query . notifications . findMany ( {
164+ where : and (
165+ eq ( Schema . notifications . type , "mention" ) ,
166+ eq ( Schema . notifications . targetPostId , replyPost . id ) ,
167+ ) ,
168+ } ) ;
169+
170+ expect ( notifications ) . toHaveLength ( 1 ) ;
171+ } ) ;
172+ } ) ;
173+
76174describe ( "Quote notifications" , ( ) => {
77175 let localAccount : Awaited < ReturnType < typeof createAccount > > ;
78176 let remoteAccount : Schema . Account ;
@@ -104,11 +202,9 @@ describe("Quote notifications", () => {
104202 expect ( originalPostWithAccount ) . not . toBeNull ( ) ;
105203
106204 // Create quote post by remote user
107- const quotePost = await createPost (
108- remoteAccount . id ,
109- "Quoting this!" ,
110- originalPost . id ,
111- ) ;
205+ const quotePost = await createPost ( remoteAccount . id , "Quoting this!" , {
206+ quoteTargetId : originalPost . id ,
207+ } ) ;
112208
113209 // Create quote notification
114210 const notificationId = await createQuoteNotification (
@@ -157,7 +253,7 @@ describe("Quote notifications", () => {
157253 const quotePost = await createPost (
158254 localAccount . id as Uuid ,
159255 "Quoting my own post" ,
160- originalPost . id ,
256+ { quoteTargetId : originalPost . id } ,
161257 ) ;
162258
163259 // Create quote notification - should return null for self-quote
@@ -188,11 +284,9 @@ describe("Quote notifications", () => {
188284 const anotherRemote = await createRemoteAccount ( "another_remote" ) ;
189285
190286 // Create quote post
191- const quotePost = await createPost (
192- anotherRemote . id ,
193- "Quoting remote" ,
194- originalPost . id ,
195- ) ;
287+ const quotePost = await createPost ( anotherRemote . id , "Quoting remote" , {
288+ quoteTargetId : originalPost . id ,
289+ } ) ;
196290
197291 // Create quote notification - should return null (original author not local)
198292 const notificationId = await createQuoteNotification (
@@ -213,11 +307,9 @@ describe("Quote notifications", () => {
213307 const originalPost = await createPost ( remoteAccount . id , "Original post" ) ;
214308
215309 // Local user creates a quote post
216- const quotePost = await createPost (
217- localAccount . id as Uuid ,
218- "My quote" ,
219- originalPost . id ,
220- ) ;
310+ const quotePost = await createPost ( localAccount . id as Uuid , "My quote" , {
311+ quoteTargetId : originalPost . id ,
312+ } ) ;
221313
222314 // Get local account with owner info
223315 const quoteAuthor = await db . query . accounts . findFirst ( {
@@ -309,11 +401,9 @@ describe("Quote notifications", () => {
309401 } ) ;
310402
311403 // Create quote post by remote user
312- const quotePost = await createPost (
313- remoteAccount . id ,
314- "Quoting this!" ,
315- originalPost . id ,
316- ) ;
404+ const quotePost = await createPost ( remoteAccount . id , "Quoting this!" , {
405+ quoteTargetId : originalPost . id ,
406+ } ) ;
317407
318408 // Create quote notification twice
319409 const notificationId1 = await createQuoteNotification (
0 commit comments