File tree Expand file tree Collapse file tree 2 files changed +37
-2
lines changed
backend/tests/Taskdeck.Api.Tests
frontend/taskdeck-web/src/tests/views Expand file tree Collapse file tree 2 files changed +37
-2
lines changed Original file line number Diff line number Diff line change @@ -40,13 +40,17 @@ public async Task AuthEndpoints_ShouldThrottleAfterBurst_ByClientIp()
4040 [ Fact ]
4141 public async Task AuthEndpoints_ShouldRecoverAfterWindowReset ( )
4242 {
43+ // Use a 3-second window so CI slowness cannot reset the window between the
44+ // setup request and the throttle probe — one permit is consumed by the first
45+ // SendInvalidLoginAsync call and the window must stay active long enough for
46+ // SendInvalidLoginUntilThrottledAsync to observe the 429.
4347 using var factory = CreateFactoryWithRateLimits (
4448 authPermitLimit : 1 ,
45- authWindowSeconds : 1 ) ;
49+ authWindowSeconds : 3 ) ;
4650 using var client = factory . CreateClient ( ) ;
4751
4852 ( await SendInvalidLoginAsync ( client ) ) . StatusCode . Should ( ) . Be ( HttpStatusCode . Unauthorized ) ;
49- var throttled = await SendInvalidLoginUntilThrottledAsync ( client ) ;
53+ var throttled = await SendInvalidLoginUntilThrottledAsync ( client , maxAttempts : 15 ) ;
5054 var retryAfterSeconds = await AssertThrottleContractAsync ( throttled , RateLimitingPolicyNames . AuthPerIp ) ;
5155 using var recovered = await SendInvalidLoginUntilRecoveredAsync ( client , retryAfterSeconds ) ;
5256 recovered . StatusCode . Should ( ) . Be ( HttpStatusCode . Unauthorized ) ;
Original file line number Diff line number Diff line change @@ -231,6 +231,37 @@ describe('ArchiveView', () => {
231231 setItemSpy . mockRestore ( )
232232 } )
233233
234+ it ( 'shows error toast when board restore fails' , async ( ) => {
235+ const confirmSpy = vi . spyOn ( window , 'confirm' ) . mockReturnValue ( true )
236+
237+ mocks . getBoards . mockResolvedValue ( [
238+ {
239+ id : 'board-archived' ,
240+ name : 'Failing Board' ,
241+ description : null ,
242+ isArchived : true ,
243+ createdAt : new Date ( ) . toISOString ( ) ,
244+ updatedAt : new Date ( ) . toISOString ( ) ,
245+ } ,
246+ ] )
247+ mocks . updateBoard . mockRejectedValue ( new Error ( 'network error' ) )
248+
249+ const wrapper = mount ( ArchiveView )
250+ await waitForAsyncUi ( )
251+
252+ const restoreBoardButton = findButtonByText ( wrapper , 'Restore Board' )
253+ await restoreBoardButton . trigger ( 'click' )
254+ await waitForAsyncUi ( )
255+
256+ expect ( mocks . updateBoard ) . toHaveBeenCalledWith ( 'board-archived' , { isArchived : false } )
257+ expect ( mocks . successToast ) . not . toHaveBeenCalled ( )
258+ expect ( mocks . errorToast ) . toHaveBeenCalledWith ( 'Failed to restore board' )
259+ // Board remains in the list
260+ expect ( wrapper . findAll ( '.td-archive-list--section .td-archive-row' ) ) . toHaveLength ( 1 )
261+
262+ confirmSpy . mockRestore ( )
263+ } )
264+
234265 it ( 'hides archived board from default list and reveals it when hidden toggle is enabled' , async ( ) => {
235266 mocks . getBoards . mockResolvedValue ( [
236267 {
You can’t perform that action at this time.
0 commit comments