11import { beforeEach , describe , expect , it , vi } from "vitest" ;
2+
3+ const drizzleMocks = vi . hoisted ( ( ) => ( {
4+ and : vi . fn ( ) ,
5+ eq : vi . fn ( ) ,
6+ } ) ) ;
7+
8+ vi . mock ( "drizzle-orm" , async ( importOriginal ) => {
9+ const actual = await importOriginal < typeof import ( "drizzle-orm" ) > ( ) ;
10+ return {
11+ ...actual ,
12+ and : drizzleMocks . and ,
13+ eq : drizzleMocks . eq ,
14+ } ;
15+ } ) ;
16+
217import type {
318 NewUserNotification ,
419 SavedJob ,
@@ -94,6 +109,11 @@ describe("NotificationsService", () => {
94109 beforeEach ( ( ) => {
95110 tx = makeTx ( ) ;
96111 service = new NotificationsService ( tx as any ) ;
112+ drizzleMocks . and . mockImplementation ( ( ...conditions ) => conditions ) ;
113+ drizzleMocks . eq . mockImplementation ( ( column , value ) => ( {
114+ column,
115+ value,
116+ } ) ) ;
97117 } ) ;
98118
99119 it ( "lista notificações com filtros de canal e não lidas" , async ( ) => {
@@ -113,6 +133,11 @@ describe("NotificationsService", () => {
113133 } ) ;
114134 expect ( listBuilder . limit ) . toHaveBeenCalledWith ( 10 ) ;
115135 expect ( tx . select ) . toHaveBeenCalledTimes ( 2 ) ;
136+ expect ( listBuilder . where ) . toHaveBeenCalledWith (
137+ expect . arrayContaining ( [
138+ { column : expect . anything ( ) , value : "user-1" } ,
139+ ] ) ,
140+ ) ;
116141 } ) ;
117142
118143 it ( "usa unreadCount zero quando a consulta agregada não retorna linhas" , async ( ) => {
@@ -155,6 +180,10 @@ describe("NotificationsService", () => {
155180 service . markRead ( "user-1" , "notification-1" ) ,
156181 ) . resolves . toBe ( notification ) ;
157182 expect ( update . set ) . toHaveBeenCalledWith ( { readAt : expect . any ( Date ) } ) ;
183+ expect ( update . where ) . toHaveBeenCalledWith ( [
184+ { column : expect . anything ( ) , value : "notification-1" } ,
185+ { column : expect . anything ( ) , value : "user-1" } ,
186+ ] ) ;
158187 } ) ;
159188
160189 it ( "lança not found ao marcar como lida quando não encontra a notificação" , async ( ) => {
@@ -176,6 +205,12 @@ describe("NotificationsService", () => {
176205 await expect ( service . markAllRead ( "user-1" , "message" ) ) . resolves . toEqual ( {
177206 updated : 2 ,
178207 } ) ;
208+ expect ( update . where ) . toHaveBeenCalledWith (
209+ expect . arrayContaining ( [
210+ { column : expect . anything ( ) , value : "user-1" } ,
211+ { column : expect . anything ( ) , value : "message" } ,
212+ ] ) ,
213+ ) ;
179214 } ) ;
180215
181216 it ( "limpa notificações de um canal" , async ( ) => {
@@ -187,6 +222,12 @@ describe("NotificationsService", () => {
187222 } ) ;
188223
189224 expect ( tx . delete ) . toHaveBeenCalled ( ) ;
225+ expect ( deleteQuery . where ) . toHaveBeenCalledWith (
226+ expect . arrayContaining ( [
227+ { column : expect . anything ( ) , value : "user-1" } ,
228+ { column : expect . anything ( ) , value : "notification" } ,
229+ ] ) ,
230+ ) ;
190231 expect ( deleteQuery . returning ) . toHaveBeenCalledWith ( {
191232 id : expect . anything ( ) ,
192233 } ) ;
@@ -326,4 +367,32 @@ describe("NotificationsService", () => {
326367 } ) ,
327368 ) ;
328369 } ) ;
370+
371+ it ( "deduplica alto match usando userId para impedir colisão cross-user" , async ( ) => {
372+ tx . query . userNotifications . findFirst . mockResolvedValue ( notification ) ;
373+
374+ const result = await service . createHighMatchIfMissing ( "user-1" , {
375+ id : "job-1" ,
376+ title : "Backend Developer" ,
377+ company : "Candidate" ,
378+ link : "https://jobs.test/1" ,
379+ matchScore : 95 ,
380+ } ) ;
381+
382+ expect ( result ) . toBe ( notification ) ;
383+
384+ const where = tx . query . userNotifications . findFirst . mock . calls [ 0 ] [ 0 ] . where ;
385+ const table = {
386+ userId : "userNotifications.userId" ,
387+ type : "userNotifications.type" ,
388+ entityType : "userNotifications.entityType" ,
389+ entityId : "userNotifications.entityId" ,
390+ } ;
391+ expect ( where ( table , drizzleMocks ) ) . toEqual ( [
392+ { column : "userNotifications.userId" , value : "user-1" } ,
393+ { column : "userNotifications.type" , value : "high_match" } ,
394+ { column : "userNotifications.entityType" , value : "job" } ,
395+ { column : "userNotifications.entityId" , value : "job-1" } ,
396+ ] ) ;
397+ } ) ;
329398} ) ;
0 commit comments