@@ -35,21 +35,21 @@ describe("TelemetryService circuit breaker", () => {
3535 expect ( mockClient . capture ) . toHaveBeenCalledTimes ( 49 )
3636 } )
3737
38- it ( "trips after 50 CODE_INDEX_ERROR captures within the window and drops further ones" , ( ) => {
38+ it ( "trips at the 50th CODE_INDEX_ERROR capture within the window and drops further ones" , ( ) => {
3939 const service = new TelemetryService ( [ mockClient ] )
4040
41- for ( let i = 0 ; i < 50 ; i ++ ) {
41+ for ( let i = 0 ; i < 49 ; i ++ ) {
4242 service . captureEvent ( TelemetryEventName . CODE_INDEX_ERROR , { i } )
4343 }
44- expect ( mockClient . capture ) . toHaveBeenCalledTimes ( 50 )
44+ expect ( mockClient . capture ) . toHaveBeenCalledTimes ( 49 )
4545
46- // 51st capture should be dropped - breaker has tripped .
47- service . captureEvent ( TelemetryEventName . CODE_INDEX_ERROR , { i : 50 } )
48- expect ( mockClient . capture ) . toHaveBeenCalledTimes ( 50 )
46+ // 50th capture trips the breaker but is itself dropped .
47+ service . captureEvent ( TelemetryEventName . CODE_INDEX_ERROR , { i : 49 } )
48+ expect ( mockClient . capture ) . toHaveBeenCalledTimes ( 49 )
4949
5050 // Keeps dropping while tripped.
51- service . captureEvent ( TelemetryEventName . CODE_INDEX_ERROR , { i : 51 } )
52- expect ( mockClient . capture ) . toHaveBeenCalledTimes ( 50 )
51+ service . captureEvent ( TelemetryEventName . CODE_INDEX_ERROR , { i : 50 } )
52+ expect ( mockClient . capture ) . toHaveBeenCalledTimes ( 49 )
5353 } )
5454
5555 it ( "re-allows captures after the cooldown window elapses" , ( ) => {
@@ -58,18 +58,17 @@ describe("TelemetryService circuit breaker", () => {
5858 for ( let i = 0 ; i < 50 ; i ++ ) {
5959 service . captureEvent ( TelemetryEventName . CODE_INDEX_ERROR , { i } )
6060 }
61- service . captureEvent ( TelemetryEventName . CODE_INDEX_ERROR , { i : 50 } )
62- expect ( mockClient . capture ) . toHaveBeenCalledTimes ( 50 )
61+ expect ( mockClient . capture ) . toHaveBeenCalledTimes ( 49 )
6362
6463 // Just under 10 minutes - still tripped.
6564 vi . setSystemTime ( 10 * 60 * 1000 - 1 )
66- service . captureEvent ( TelemetryEventName . CODE_INDEX_ERROR , { i : 51 } )
67- expect ( mockClient . capture ) . toHaveBeenCalledTimes ( 50 )
65+ service . captureEvent ( TelemetryEventName . CODE_INDEX_ERROR , { i : 50 } )
66+ expect ( mockClient . capture ) . toHaveBeenCalledTimes ( 49 )
6867
6968 // Cooldown elapsed - one more error gets through.
7069 vi . setSystemTime ( 10 * 60 * 1000 )
71- service . captureEvent ( TelemetryEventName . CODE_INDEX_ERROR , { i : 52 } )
72- expect ( mockClient . capture ) . toHaveBeenCalledTimes ( 51 )
70+ service . captureEvent ( TelemetryEventName . CODE_INDEX_ERROR , { i : 51 } )
71+ expect ( mockClient . capture ) . toHaveBeenCalledTimes ( 50 )
7372 } )
7473
7574 it ( "does not reset the guarded count when unrelated events are interleaved" , ( ) => {
@@ -85,17 +84,23 @@ describe("TelemetryService circuit breaker", () => {
8584 // 25 CODE_INDEX_ERROR so far - still under the threshold of 50.
8685 expect ( mockClient . capture ) . toHaveBeenCalledTimes ( 25 + 25 )
8786
88- for ( let i = 25 ; i < 50 ; i ++ ) {
87+ for ( let i = 25 ; i < 49 ; i ++ ) {
8988 service . captureEvent ( TelemetryEventName . CODE_INDEX_ERROR , { i } )
9089 service . captureEvent ( TelemetryEventName . TASK_CREATED , { taskId : `task-${ i } ` } )
9190 }
92- // 50th CODE_INDEX_ERROR trips the breaker; TASK_CREATED events are never guarded.
93- expect ( mockClient . capture ) . toHaveBeenCalledTimes ( 50 + 50 )
91+ // 49 CODE_INDEX_ERROR so far - still under the threshold of 50.
92+ expect ( mockClient . capture ) . toHaveBeenCalledTimes ( 49 + 49 )
93+
94+ // 50th CODE_INDEX_ERROR trips the breaker (and is itself dropped); TASK_CREATED
95+ // events are never guarded.
96+ service . captureEvent ( TelemetryEventName . CODE_INDEX_ERROR , { i : 49 } )
97+ service . captureEvent ( TelemetryEventName . TASK_CREATED , { taskId : "task-49" } )
98+ expect ( mockClient . capture ) . toHaveBeenCalledTimes ( 49 + 50 )
9499
95100 // Further CODE_INDEX_ERROR captures are dropped even though unrelated events keep flowing.
96101 service . captureEvent ( TelemetryEventName . CODE_INDEX_ERROR , { i : 50 } )
97102 service . captureEvent ( TelemetryEventName . TASK_CREATED , { taskId : "task-50" } )
98- expect ( mockClient . capture ) . toHaveBeenCalledTimes ( 50 + 51 )
103+ expect ( mockClient . capture ) . toHaveBeenCalledTimes ( 49 + 51 )
99104 } )
100105
101106 it ( "expires old occurrences outside the counting window instead of trapping the breaker open forever" , ( ) => {
@@ -121,4 +126,16 @@ describe("TelemetryService circuit breaker", () => {
121126
122127 expect ( mockClient . capture ) . toHaveBeenCalledTimes ( 200 )
123128 } )
129+
130+ it ( "returns early on the not-ready (zero-client) branch without touching circuit breaker state" , ( ) => {
131+ // captureEvent's !this.isReady check runs before shouldDropForCircuitBreaker, so with
132+ // no clients registered, guarded-event bookkeeping should never be reached.
133+ const service = new TelemetryService ( [ ] )
134+
135+ for ( let i = 0 ; i < 50 ; i ++ ) {
136+ service . captureEvent ( TelemetryEventName . CODE_INDEX_ERROR , { i } )
137+ }
138+
139+ expect ( mockClient . capture ) . not . toHaveBeenCalled ( )
140+ } )
124141} )
0 commit comments