@@ -40,12 +40,15 @@ describe("AutoApprovalHandler", () => {
4040 mockState . allowedMaxCost = 10
4141 const messages : ClineMessage [ ] = [ ]
4242
43- // First call should be under limit
43+ // First call should be under limit (count = 1)
4444 const result1 = await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
4545 expect ( result1 . shouldProceed ) . toBe ( true )
4646 expect ( result1 . requiresApproval ) . toBe ( false )
4747
48- // Second call should trigger request limit
48+ // Add a message to simulate first request completed
49+ messages . push ( { type : "say" , say : "api_req_started" , text : "{}" , ts : 1000 } )
50+
51+ // Second call should trigger request limit (1 message + current = 2 > 1)
4952 mockAskForApproval . mockResolvedValue ( { response : "yesButtonClicked" } )
5053 const result2 = await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
5154
@@ -64,27 +67,35 @@ describe("AutoApprovalHandler", () => {
6467 mockState . allowedMaxRequests = 3
6568 } )
6669
67- it ( "should increment request count on each check " , async ( ) => {
70+ it ( "should calculate request count from messages " , async ( ) => {
6871 const messages : ClineMessage [ ] = [ ]
6972
70- // Check state after each call
71- for ( let i = 1 ; i <= 3 ; i ++ ) {
72- await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
73- const state = handler . getApprovalState ( )
74- expect ( state . requestCount ) . toBe ( i )
75- }
73+ // First check - no messages yet, count should be 1 (for current request)
74+ await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
75+ let state = handler . getApprovalState ( )
76+ expect ( state . requestCount ) . toBe ( 1 )
77+
78+ // Add API request messages
79+ messages . push ( { type : "say" , say : "api_req_started" , text : "{}" , ts : 1000 } )
80+ await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
81+ state = handler . getApprovalState ( )
82+ expect ( state . requestCount ) . toBe ( 2 ) // 1 message + current request
83+
84+ messages . push ( { type : "say" , say : "api_req_started" , text : "{}" , ts : 2000 } )
85+ await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
86+ state = handler . getApprovalState ( )
87+ expect ( state . requestCount ) . toBe ( 3 ) // 2 messages + current request
7688 } )
7789
7890 it ( "should ask for approval when limit is exceeded" , async ( ) => {
7991 const messages : ClineMessage [ ] = [ ]
8092
81- // Make 3 requests (within limit )
93+ // Add 3 API request messages (to simulate 3 requests made )
8294 for ( let i = 0 ; i < 3 ; i ++ ) {
83- await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
95+ messages . push ( { type : "say" , say : "api_req_started" , text : "{}" , ts : 1000 + i } )
8496 }
85- expect ( mockAskForApproval ) . not . toHaveBeenCalled ( )
8697
87- // 4th request should trigger approval
98+ // Next check should trigger approval (3 messages + current = 4 > 3)
8899 mockAskForApproval . mockResolvedValue ( { response : "yesButtonClicked" } )
89100 const result = await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
90101
@@ -99,29 +110,35 @@ describe("AutoApprovalHandler", () => {
99110 it ( "should reset count when user approves" , async ( ) => {
100111 const messages : ClineMessage [ ] = [ ]
101112
102- // Exceed limit
113+ // Add messages to exceed limit
103114 for ( let i = 0 ; i < 3 ; i ++ ) {
104- await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
115+ messages . push ( { type : "say" , say : "api_req_started" , text : "{}" , ts : 1000 + i } )
105116 }
106117
107- // 4th request should trigger approval and reset
118+ // Next request should trigger approval and reset
108119 mockAskForApproval . mockResolvedValue ( { response : "yesButtonClicked" } )
109120 await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
110121
111- // Count should be reset
122+ // Add more messages after reset
123+ messages . push ( { type : "say" , say : "api_req_started" , text : "{}" , ts : 4000 } )
124+
125+ // Next check should only count messages after reset
126+ const result = await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
127+ expect ( result . requiresApproval ) . toBe ( false ) // Should not require approval (1 message + current = 2 <= 3)
128+
112129 const state = handler . getApprovalState ( )
113- expect ( state . requestCount ) . toBe ( 0 )
130+ expect ( state . requestCount ) . toBe ( 2 ) // 1 message after reset + current request
114131 } )
115132
116133 it ( "should not proceed when user rejects" , async ( ) => {
117134 const messages : ClineMessage [ ] = [ ]
118135
119- // Exceed limit
136+ // Add messages to exceed limit
120137 for ( let i = 0 ; i < 3 ; i ++ ) {
121- await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
138+ messages . push ( { type : "say" , say : "api_req_started" , text : "{}" , ts : 1000 + i } )
122139 }
123140
124- // 4th request with rejection
141+ // Next request with rejection
125142 mockAskForApproval . mockResolvedValue ( { response : "noButtonClicked" } )
126143 const result = await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
127144
@@ -183,17 +200,67 @@ describe("AutoApprovalHandler", () => {
183200 expect ( result3 . requiresApproval ) . toBe ( true )
184201 } )
185202
186- it ( "should not reset cost to zero on approval" , async ( ) => {
203+ it ( "should reset cost tracking on approval" , async ( ) => {
204+ const messages : ClineMessage [ ] = [
205+ { type : "say" , say : "api_req_started" , text : '{"cost": 3.0}' , ts : 1000 } ,
206+ { type : "say" , say : "api_req_started" , text : '{"cost": 3.0}' , ts : 2000 } ,
207+ ]
208+
209+ // First check - cost exceeds limit (6.0 > 5.0)
210+ mockGetApiMetrics . mockReturnValue ( { totalCost : 6.0 } )
211+ mockAskForApproval . mockResolvedValue ( { response : "yesButtonClicked" } )
212+
213+ const result1 = await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
214+ expect ( result1 . shouldProceed ) . toBe ( true )
215+ expect ( result1 . requiresApproval ) . toBe ( true )
216+
217+ // Add more messages after reset
218+ messages . push (
219+ { type : "say" , say : "api_req_started" , text : '{"cost": 2.0}' , ts : 3000 } ,
220+ { type : "say" , say : "api_req_started" , text : '{"cost": 1.0}' , ts : 4000 } ,
221+ )
222+
223+ // Second check - should only count messages after reset (3.0 < 5.0)
224+ mockGetApiMetrics . mockReturnValue ( { totalCost : 3.0 } )
225+ const result2 = await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
226+
227+ // Should not require approval since cost after reset is under limit
228+ expect ( result2 . shouldProceed ) . toBe ( true )
229+ expect ( result2 . requiresApproval ) . toBe ( false )
230+
231+ // Verify it's only calculating cost from messages after reset point
232+ expect ( mockGetApiMetrics ) . toHaveBeenLastCalledWith ( messages . slice ( 2 ) )
233+ } )
234+
235+ it ( "should track multiple cost resets correctly" , async ( ) => {
187236 const messages : ClineMessage [ ] = [ ]
188237
238+ // First cost limit hit
239+ messages . push ( { type : "say" , say : "api_req_started" , text : '{"cost": 6.0}' , ts : 1000 } )
189240 mockGetApiMetrics . mockReturnValue ( { totalCost : 6.0 } )
190241 mockAskForApproval . mockResolvedValue ( { response : "yesButtonClicked" } )
191242
192243 await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
193244
194- // Cost should still be calculated from messages, not reset
195- const state = handler . getApprovalState ( )
196- expect ( state . currentCost ) . toBe ( 6.0 )
245+ // Add more messages
246+ messages . push (
247+ { type : "say" , say : "api_req_started" , text : '{"cost": 3.0}' , ts : 2000 } ,
248+ { type : "say" , say : "api_req_started" , text : '{"cost": 3.0}' , ts : 3000 } ,
249+ )
250+
251+ // Second cost limit hit (only counting from index 1)
252+ mockGetApiMetrics . mockReturnValue ( { totalCost : 6.0 } )
253+ await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
254+
255+ // Add more messages after second reset
256+ messages . push ( { type : "say" , say : "api_req_started" , text : '{"cost": 2.0}' , ts : 4000 } )
257+
258+ // Third check - should only count from last reset
259+ mockGetApiMetrics . mockReturnValue ( { totalCost : 2.0 } )
260+ const result = await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
261+
262+ expect ( result . requiresApproval ) . toBe ( false )
263+ expect ( mockGetApiMetrics ) . toHaveBeenLastCalledWith ( messages . slice ( 3 ) )
197264 } )
198265 } )
199266
@@ -205,16 +272,21 @@ describe("AutoApprovalHandler", () => {
205272
206273 mockGetApiMetrics . mockReturnValue ( { totalCost : 3.0 } )
207274
208- // First two requests should pass
209- for ( let i = 0 ; i < 2 ; i ++ ) {
210- const result = await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
211- expect ( result . shouldProceed ) . toBe ( true )
212- expect ( result . requiresApproval ) . toBe ( false )
213- }
275+ // First request should pass (count = 1)
276+ let result = await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
277+ expect ( result . shouldProceed ) . toBe ( true )
278+ expect ( result . requiresApproval ) . toBe ( false )
214279
215- // Third request should trigger request limit (not cost limit)
280+ // Add a message and check again (count = 2)
281+ messages . push ( { type : "say" , say : "api_req_started" , text : "{}" , ts : 1000 } )
282+ result = await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
283+ expect ( result . shouldProceed ) . toBe ( true )
284+ expect ( result . requiresApproval ) . toBe ( false )
285+
286+ // Add another message - third request should trigger request limit (count = 3 > 2)
287+ messages . push ( { type : "say" , say : "api_req_started" , text : "{}" , ts : 2000 } )
216288 mockAskForApproval . mockResolvedValue ( { response : "yesButtonClicked" } )
217- const result = await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
289+ result = await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
218290
219291 expect ( mockAskForApproval ) . toHaveBeenCalledWith (
220292 "auto_approval_max_req_reached" ,
@@ -227,23 +299,38 @@ describe("AutoApprovalHandler", () => {
227299 } )
228300
229301 describe ( "resetRequestCount" , ( ) => {
230- it ( "should reset the request counter " , async ( ) => {
302+ it ( "should reset tracking " , async ( ) => {
231303 mockState . allowedMaxRequests = 5
304+ mockState . allowedMaxCost = 10.0
232305 const messages : ClineMessage [ ] = [ ]
233306
234- // Make some requests
307+ // Add some messages
235308 for ( let i = 0 ; i < 3 ; i ++ ) {
236- await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
309+ messages . push ( { type : "say" , say : "api_req_started" , text : "{}" , ts : 1000 + i } )
237310 }
238311
312+ mockGetApiMetrics . mockReturnValue ( { totalCost : 5.0 } )
313+ await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
314+
239315 let state = handler . getApprovalState ( )
240- expect ( state . requestCount ) . toBe ( 3 )
316+ expect ( state . requestCount ) . toBe ( 4 ) // 3 messages + current
317+ expect ( state . currentCost ) . toBe ( 5.0 )
241318
242319 // Reset
243320 handler . resetRequestCount ( )
244321
322+ // After reset, counts should be zero
245323 state = handler . getApprovalState ( )
246324 expect ( state . requestCount ) . toBe ( 0 )
325+ expect ( state . currentCost ) . toBe ( 0 )
326+
327+ // Next check should start fresh
328+ mockGetApiMetrics . mockReturnValue ( { totalCost : 8.0 } )
329+ await handler . checkAutoApprovalLimits ( mockState , messages , mockAskForApproval )
330+
331+ state = handler . getApprovalState ( )
332+ expect ( state . requestCount ) . toBe ( 4 ) // All messages counted again
333+ expect ( state . currentCost ) . toBe ( 8.0 )
247334 } )
248335 } )
249336} )
0 commit comments