@@ -23,6 +23,8 @@ const WEBHOOK_CHANNEL = {
2323
2424const ctx = vi . hoisted ( ( ) => ( {
2525 channels : [ ] as Array < { id : string ; type : string ; properties : unknown } > ,
26+ /** Set to model replica lag: what the replica still sees. Null means "same as primary". */
27+ replicaChannels : null as Array < { id : string ; type : string ; properties : unknown } > | null ,
2628 gateAllowed : true ,
2729 webhookFails : false ,
2830} ) ) ;
@@ -37,6 +39,11 @@ const safeWebhookFetch = vi.hoisted(() =>
3739) ;
3840
3941vi . mock ( "~/db.server" , ( ) => {
42+ const channelReader = ( rows : ( ) => Array < { id : string ; type : string ; properties : unknown } > ) => ( {
43+ findMany : async ( ) => rows ( ) ,
44+ findFirst : async ( { where } : { where : { id : string } } ) =>
45+ rows ( ) . find ( ( channel ) => channel . id === where . id ) ?? null ,
46+ } ) ;
4047 const db = {
4148 runtimeEnvironment : {
4249 findFirst : async ( ) => ( {
@@ -51,11 +58,6 @@ vi.mock("~/db.server", () => {
5158 } ,
5259 } ) ,
5360 } ,
54- projectAlertChannel : {
55- findMany : async ( ) => ctx . channels ,
56- findFirst : async ( { where } : { where : { id : string } } ) =>
57- ctx . channels . find ( ( channel ) => channel . id === where . id ) ?? null ,
58- } ,
5961 organizationIntegration : {
6062 findFirst : async ( ) => ( {
6163 id : "int_1" ,
@@ -65,7 +67,14 @@ vi.mock("~/db.server", () => {
6567 } ) ,
6668 } ,
6769 } ;
68- return { prisma : db , $replica : db , sqlDatabaseSchema : undefined } ;
70+ return {
71+ prisma : { ...db , projectAlertChannel : channelReader ( ( ) => ctx . channels ) } ,
72+ $replica : {
73+ ...db ,
74+ projectAlertChannel : channelReader ( ( ) => ctx . replicaChannels ?? ctx . channels ) ,
75+ } ,
76+ sqlDatabaseSchema : undefined ,
77+ } ;
6978} ) ;
7079
7180vi . mock ( "~/v3/canAccessDashboardAgent.server" , ( ) => ( {
@@ -111,6 +120,7 @@ const payload = {
111120
112121beforeEach ( ( ) => {
113122 ctx . channels = [ EMAIL_CHANNEL , SLACK_CHANNEL , WEBHOOK_CHANNEL ] ;
123+ ctx . replicaChannels = null ;
114124 ctx . gateAllowed = true ;
115125 ctx . webhookFails = false ;
116126 enqueue . mockClear ( ) ;
@@ -195,6 +205,18 @@ describe("dashboard agent watch alert per-channel delivery", () => {
195205 expect ( bodies [ 1 ] . created ) . toBe ( bodies [ 0 ] . created ) ;
196206 } ) ;
197207
208+ test ( "an unsubscribe the replica hasn't caught up on still stops the email" , async ( ) => {
209+ ctx . channels = [ ] ;
210+ ctx . replicaChannels = [ EMAIL_CHANNEL ] ;
211+
212+ await new DeliverDashboardAgentWatchChannelAlertService ( ) . call ( {
213+ ...payload ,
214+ channelId : "chan_email" ,
215+ } ) ;
216+
217+ expect ( sendAlertEmail ) . not . toHaveBeenCalled ( ) ;
218+ } ) ;
219+
198220 test ( "an unsubscribed channel delivers nothing" , async ( ) => {
199221 ctx . channels = [ ] ;
200222 await new DeliverDashboardAgentWatchChannelAlertService ( ) . call ( {
0 commit comments