@@ -109,80 +109,6 @@ describe("useChatUser Slice", () => {
109109 } ) ;
110110 } ) ;
111111
112- describe ( "subscribeToRoom" , ( ) => {
113- it ( "updates the user subscription list" , async ( ) => {
114- const mockUser = { _id : "test-user-id" , subscriptions : { } } ;
115-
116- mockDitto . store . execute . mockResolvedValue ( { items : [ { value : mockUser } ] } ) ;
117-
118- await store . getState ( ) . subscribeToRoom ( "room-123" ) ;
119-
120- expect ( mockDitto . store . execute ) . toHaveBeenCalledWith (
121- expect . stringContaining ( "INSERT INTO users" ) ,
122- expect . objectContaining ( {
123- newUser : expect . objectContaining ( {
124- _id : "test-user-id" ,
125- subscriptions : expect . objectContaining ( {
126- "room-123" : expect . any ( String ) ,
127- } ) ,
128- } ) ,
129- } )
130- ) ;
131- } ) ;
132-
133- it ( "does not re-subscribe if already subscribed" , async ( ) => {
134- const mockUser = {
135- _id : "test-user-id" ,
136- subscriptions : { "room-123" : "2023-01-01" }
137- } ;
138-
139- mockDitto . store . execute . mockResolvedValue ( { items : [ { value : mockUser } ] } ) ;
140-
141- await store . getState ( ) . subscribeToRoom ( "room-123" ) ;
142-
143- // Should only call execute once (for findUserById), not for update
144- expect ( mockDitto . store . execute ) . toHaveBeenCalledTimes ( 1 ) ;
145- } ) ;
146-
147- it ( "returns early when user not found" , async ( ) => {
148- mockDitto . store . execute . mockResolvedValue ( { items : [ ] } ) ;
149-
150- await store . getState ( ) . subscribeToRoom ( "room-123" ) ;
151-
152- // Should only call execute once (for findUserById)
153- expect ( mockDitto . store . execute ) . toHaveBeenCalledTimes ( 1 ) ;
154- } ) ;
155-
156- it ( "handles errors gracefully" , async ( ) => {
157- const consoleSpy = vi . spyOn ( console , "error" ) . mockImplementation ( ( ) => { } ) ;
158- mockDitto . store . execute . mockRejectedValueOnce ( new Error ( "DB Error" ) ) ;
159-
160- await store . getState ( ) . subscribeToRoom ( "room-123" ) ;
161-
162- // Error occurs in findUserById which is called internally
163- expect ( consoleSpy ) . toHaveBeenCalledWith ( "Error in findUserById:" , expect . any ( Error ) ) ;
164- consoleSpy . mockRestore ( ) ;
165- } ) ;
166-
167- it ( "handles user with no subscriptions object" , async ( ) => {
168- const mockUser = { _id : "test-user-id" } ;
169-
170- mockDitto . store . execute . mockResolvedValue ( { items : [ { value : mockUser } ] } ) ;
171-
172- await store . getState ( ) . subscribeToRoom ( "room-123" ) ;
173-
174- expect ( mockDitto . store . execute ) . toHaveBeenCalledWith (
175- expect . stringContaining ( "INSERT INTO users" ) ,
176- expect . objectContaining ( {
177- newUser : expect . objectContaining ( {
178- subscriptions : expect . objectContaining ( {
179- "room-123" : expect . any ( String ) ,
180- } ) ,
181- } ) ,
182- } )
183- ) ;
184- } ) ;
185- } ) ;
186112
187113 describe ( "markRoomAsRead" , ( ) => {
188114 it ( "updates timestamps and clears mentions" , async ( ) => {
@@ -295,8 +221,32 @@ describe("useChatUser Slice", () => {
295221 } ) ;
296222 } ) ;
297223
298- describe ( "unsubscribeFromRoom" , ( ) => {
299- it ( "removes room from subscriptions" , async ( ) => {
224+ describe ( "toggleRoomSubscription" , ( ) => {
225+ it ( "subscribes when not already subscribed" , async ( ) => {
226+ const roomId = "room-123" ;
227+ const mockUser = {
228+ _id : "test-user-id" ,
229+ subscriptions : { "other-room" : "2023-01-01" } ,
230+ } ;
231+
232+ mockDitto . store . execute . mockResolvedValue ( { items : [ { value : mockUser } ] } ) ;
233+
234+ await store . getState ( ) . toggleRoomSubscription ( roomId ) ;
235+
236+ expect ( mockDitto . store . execute ) . toHaveBeenCalledWith (
237+ expect . stringContaining ( "INSERT INTO users" ) ,
238+ expect . objectContaining ( {
239+ newUser : expect . objectContaining ( {
240+ subscriptions : expect . objectContaining ( {
241+ [ roomId ] : expect . any ( String ) ,
242+ "other-room" : "2023-01-01" ,
243+ } ) ,
244+ } ) ,
245+ } )
246+ ) ;
247+ } ) ;
248+
249+ it ( "unsubscribes when already subscribed" , async ( ) => {
300250 const roomId = "room-to-leave" ;
301251
302252 const mockUser = {
@@ -305,32 +255,40 @@ describe("useChatUser Slice", () => {
305255 } ;
306256 mockDitto . store . execute . mockResolvedValue ( { items : [ { value : mockUser } ] } ) ;
307257
308- await store . getState ( ) . unsubscribeFromRoom ( roomId ) ;
258+ await store . getState ( ) . toggleRoomSubscription ( roomId ) ;
309259
310260 expect ( mockDitto . store . execute ) . toHaveBeenCalledWith (
311261 expect . stringContaining ( "INSERT INTO users" ) ,
312262 expect . objectContaining ( {
313263 newUser : expect . objectContaining ( {
314- subscriptions : { "other-room" : "2023-01-01" } ,
264+ subscriptions : { "room-to-leave" : null , " other-room" : "2023-01-01" } ,
315265 } ) ,
316266 } )
317267 ) ;
318268 } ) ;
319269
320- it ( "returns early when user not found" , async ( ) => {
321- mockDitto . store . execute . mockResolvedValue ( { items : [ ] } ) ;
270+ it ( "subscribes when user has no subscriptions object" , async ( ) => {
271+ const mockUser = { _id : "test-user-id" } ;
272+ mockDitto . store . execute . mockResolvedValue ( { items : [ { value : mockUser } ] } ) ;
322273
323- await store . getState ( ) . unsubscribeFromRoom ( "room-123" ) ;
274+ await store . getState ( ) . toggleRoomSubscription ( "room-123" ) ;
324275
325- // Should only call execute once (for findUserById)
326- expect ( mockDitto . store . execute ) . toHaveBeenCalledTimes ( 1 ) ;
276+ expect ( mockDitto . store . execute ) . toHaveBeenCalledWith (
277+ expect . stringContaining ( "INSERT INTO users" ) ,
278+ expect . objectContaining ( {
279+ newUser : expect . objectContaining ( {
280+ subscriptions : expect . objectContaining ( {
281+ "room-123" : expect . any ( String ) ,
282+ } ) ,
283+ } ) ,
284+ } )
285+ ) ;
327286 } ) ;
328287
329- it ( "returns early when user has no subscriptions" , async ( ) => {
330- const mockUser = { _id : "test-user-id" } ;
331- mockDitto . store . execute . mockResolvedValue ( { items : [ { value : mockUser } ] } ) ;
288+ it ( "returns early when user not found" , async ( ) => {
289+ mockDitto . store . execute . mockResolvedValue ( { items : [ ] } ) ;
332290
333- await store . getState ( ) . unsubscribeFromRoom ( "room-123" ) ;
291+ await store . getState ( ) . toggleRoomSubscription ( "room-123" ) ;
334292
335293 // Should only call execute once (for findUserById)
336294 expect ( mockDitto . store . execute ) . toHaveBeenCalledTimes ( 1 ) ;
@@ -340,7 +298,7 @@ describe("useChatUser Slice", () => {
340298 const consoleSpy = vi . spyOn ( console , "error" ) . mockImplementation ( ( ) => { } ) ;
341299 mockDitto . store . execute . mockRejectedValueOnce ( new Error ( "DB Error" ) ) ;
342300
343- await store . getState ( ) . unsubscribeFromRoom ( "room-123" ) ;
301+ await store . getState ( ) . toggleRoomSubscription ( "room-123" ) ;
344302
345303 // Error occurs in findUserById which is called internally
346304 expect ( consoleSpy ) . toHaveBeenCalledWith ( "Error in findUserById:" , expect . any ( Error ) ) ;
@@ -453,15 +411,6 @@ describe("useChatUser Slice", () => {
453411 expect ( mockDitto . store . execute ) . not . toHaveBeenCalled ( ) ;
454412 } ) ;
455413
456- it ( "subscribeToRoom returns early when ditto is null" , async ( ) => {
457- const storeWithoutDitto = createTestStore ( null ) ;
458-
459- await storeWithoutDitto . getState ( ) . subscribeToRoom ( "room-123" ) ;
460-
461- // Should not throw and should not call execute
462- expect ( mockDitto . store . execute ) . not . toHaveBeenCalled ( ) ;
463- } ) ;
464-
465414 it ( "markRoomAsRead returns early when ditto is null" , async ( ) => {
466415 const storeWithoutDitto = createTestStore ( null ) ;
467416
@@ -471,10 +420,10 @@ describe("useChatUser Slice", () => {
471420 expect ( mockDitto . store . execute ) . not . toHaveBeenCalled ( ) ;
472421 } ) ;
473422
474- it ( "unsubscribeFromRoom returns early when ditto is null" , async ( ) => {
423+ it ( "toggleRoomSubscription returns early when ditto is null" , async ( ) => {
475424 const storeWithoutDitto = createTestStore ( null ) ;
476425
477- await storeWithoutDitto . getState ( ) . unsubscribeFromRoom ( "room-123" ) ;
426+ await storeWithoutDitto . getState ( ) . toggleRoomSubscription ( "room-123" ) ;
478427
479428 // Should not throw and should not call execute
480429 expect ( mockDitto . store . execute ) . not . toHaveBeenCalled ( ) ;
0 commit comments