Skip to content

Commit cebfb82

Browse files
KyleAMathewsclaude
andcommitted
Add tests for iteration tracker utility
Verifies the iteration breakdown output format and state tracking logic used by circuit breaker diagnostics. Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent b9a2cf7 commit cebfb82

File tree

1 file changed

+173
-0
lines changed

1 file changed

+173
-0
lines changed
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { createIterationTracker } from '../src/iteration-tracker.js'
3+
4+
describe(`createIterationTracker`, () => {
5+
it(`should not exceed limit on normal iteration counts`, () => {
6+
const tracker = createIterationTracker<string>(100)
7+
8+
for (let i = 0; i < 50; i++) {
9+
expect(tracker.trackAndCheckLimit(`state-a`)).toBe(false)
10+
}
11+
12+
expect(tracker.getIterations()).toBe(50)
13+
})
14+
15+
it(`should return true when limit is exceeded`, () => {
16+
const tracker = createIterationTracker<string>(10)
17+
18+
for (let i = 0; i < 10; i++) {
19+
expect(tracker.trackAndCheckLimit(`state`)).toBe(false)
20+
}
21+
22+
// 11th iteration exceeds the limit
23+
expect(tracker.trackAndCheckLimit(`state`)).toBe(true)
24+
expect(tracker.getIterations()).toBe(11)
25+
})
26+
27+
it(`should track state transitions correctly`, () => {
28+
const tracker = createIterationTracker<string>(100)
29+
30+
// 3 iterations in state-a
31+
tracker.trackAndCheckLimit(`state-a`)
32+
tracker.trackAndCheckLimit(`state-a`)
33+
tracker.trackAndCheckLimit(`state-a`)
34+
35+
// 2 iterations in state-b
36+
tracker.trackAndCheckLimit(`state-b`)
37+
tracker.trackAndCheckLimit(`state-b`)
38+
39+
// 1 iteration in state-c (forces recording of state-b)
40+
tracker.trackAndCheckLimit(`state-c`)
41+
42+
const history = tracker.getHistory()
43+
44+
expect(history).toHaveLength(2)
45+
expect(history[0]).toEqual({
46+
state: `state-a`,
47+
startIter: 1,
48+
endIter: 3,
49+
})
50+
expect(history[1]).toEqual({
51+
state: `state-b`,
52+
startIter: 4,
53+
endIter: 5,
54+
})
55+
})
56+
57+
it(`should record final state when limit is exceeded`, () => {
58+
const tracker = createIterationTracker<string>(5)
59+
60+
// 2 iterations in state-a
61+
tracker.trackAndCheckLimit(`state-a`)
62+
tracker.trackAndCheckLimit(`state-a`)
63+
64+
// 4 iterations in state-b (exceeds limit at iteration 6)
65+
tracker.trackAndCheckLimit(`state-b`)
66+
tracker.trackAndCheckLimit(`state-b`)
67+
tracker.trackAndCheckLimit(`state-b`)
68+
const exceeded = tracker.trackAndCheckLimit(`state-b`)
69+
70+
expect(exceeded).toBe(true)
71+
72+
const history = tracker.getHistory()
73+
expect(history).toHaveLength(2)
74+
expect(history[0]).toEqual({
75+
state: `state-a`,
76+
startIter: 1,
77+
endIter: 2,
78+
})
79+
expect(history[1]).toEqual({
80+
state: `state-b`,
81+
startIter: 3,
82+
endIter: 6,
83+
})
84+
})
85+
86+
it(`should format warning with iteration breakdown`, () => {
87+
const tracker = createIterationTracker<string>(5, (state) => `ops=[${state}]`)
88+
89+
// Create a pattern: 2 in state-a, then exceed in state-b
90+
tracker.trackAndCheckLimit(`TopK,Filter`)
91+
tracker.trackAndCheckLimit(`TopK,Filter`)
92+
tracker.trackAndCheckLimit(`TopK`)
93+
tracker.trackAndCheckLimit(`TopK`)
94+
tracker.trackAndCheckLimit(`TopK`)
95+
tracker.trackAndCheckLimit(`TopK`) // exceeds
96+
97+
const warning = tracker.formatWarning(`D2 graph execution`, {
98+
totalOperators: 8,
99+
})
100+
101+
expect(warning).toContain(`[TanStack DB] D2 graph execution exceeded 5 iterations`)
102+
expect(warning).toContain(`Continuing with available data`)
103+
expect(warning).toContain(`Iteration breakdown (where the loop spent time):`)
104+
expect(warning).toContain(`1-2: ops=[TopK,Filter]`)
105+
expect(warning).toContain(`3-6: ops=[TopK]`)
106+
expect(warning).toContain(`"totalOperators": 8`)
107+
expect(warning).toContain(`https://github.com/TanStack/db/issues`)
108+
})
109+
110+
it(`should work with object states using default JSON serialization`, () => {
111+
type State = { valuesNeeded: number; keysInBatch: number }
112+
const tracker = createIterationTracker<State>(10)
113+
114+
tracker.trackAndCheckLimit({ valuesNeeded: 10, keysInBatch: 5 })
115+
tracker.trackAndCheckLimit({ valuesNeeded: 10, keysInBatch: 5 })
116+
tracker.trackAndCheckLimit({ valuesNeeded: 8, keysInBatch: 3 })
117+
118+
const history = tracker.getHistory()
119+
expect(history).toHaveLength(1)
120+
expect(history[0]!.state).toEqual({ valuesNeeded: 10, keysInBatch: 5 })
121+
})
122+
123+
it(`should use custom stateToKey function for display`, () => {
124+
type State = { valuesNeeded: number; keysInBatch: number }
125+
const tracker = createIterationTracker<State>(
126+
5,
127+
(state) => `valuesNeeded=${state.valuesNeeded}, keysInBatch=${state.keysInBatch}`
128+
)
129+
130+
tracker.trackAndCheckLimit({ valuesNeeded: 10, keysInBatch: 5 })
131+
tracker.trackAndCheckLimit({ valuesNeeded: 10, keysInBatch: 5 })
132+
tracker.trackAndCheckLimit({ valuesNeeded: 10, keysInBatch: 5 })
133+
tracker.trackAndCheckLimit({ valuesNeeded: 10, keysInBatch: 5 })
134+
tracker.trackAndCheckLimit({ valuesNeeded: 10, keysInBatch: 5 })
135+
tracker.trackAndCheckLimit({ valuesNeeded: 10, keysInBatch: 5 }) // exceeds
136+
137+
const warning = tracker.formatWarning(`requestLimitedSnapshot`)
138+
139+
expect(warning).toContain(`1-6: valuesNeeded=10, keysInBatch=5`)
140+
})
141+
142+
it(`should handle single state that exceeds limit`, () => {
143+
const tracker = createIterationTracker<string>(3)
144+
145+
tracker.trackAndCheckLimit(`stuck`)
146+
tracker.trackAndCheckLimit(`stuck`)
147+
tracker.trackAndCheckLimit(`stuck`)
148+
const exceeded = tracker.trackAndCheckLimit(`stuck`)
149+
150+
expect(exceeded).toBe(true)
151+
152+
const history = tracker.getHistory()
153+
expect(history).toHaveLength(1)
154+
expect(history[0]).toEqual({
155+
state: `stuck`,
156+
startIter: 1,
157+
endIter: 4,
158+
})
159+
})
160+
161+
it(`should format warning without diagnostic info`, () => {
162+
const tracker = createIterationTracker<string>(2)
163+
164+
tracker.trackAndCheckLimit(`state`)
165+
tracker.trackAndCheckLimit(`state`)
166+
tracker.trackAndCheckLimit(`state`) // exceeds
167+
168+
const warning = tracker.formatWarning(`Graph execution`)
169+
170+
expect(warning).toContain(`[TanStack DB] Graph execution exceeded 2 iterations`)
171+
expect(warning).not.toContain(`Diagnostic info:`)
172+
})
173+
})

0 commit comments

Comments
 (0)