test(bf-27e4): add test coverage for beadsCompleted vs stuck detection metric
- Add test case for worker processing 100 beads with 0 successful completions - Fix incorrect test expecting beadsCompleted to increment on bead.released - beadsCompleted only increments on bead.completed events - beadsReleased increments on bead.released with release_success - Stuck detection now uses unified beadsCompleted metric with clear messaging Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
04904ce032
commit
c047131e09
2 changed files with 53 additions and 2 deletions
|
|
@ -227,12 +227,13 @@ describe('InMemoryEventStore', () => {
|
|||
expect(worker?.status).toBe('idle');
|
||||
});
|
||||
|
||||
it('should increment beadsCompleted on bead.released with release_success', () => {
|
||||
it('should increment beadsReleased on bead.released with release_success', () => {
|
||||
store.add(createEvent({ worker: 'w-test', msg: 'bead.released', reason: 'release_success', bead: 'bd-1' }));
|
||||
store.add(createEvent({ worker: 'w-test', msg: 'bead.released', reason: 'release_success', bead: 'bd-2' }));
|
||||
|
||||
const worker = store.getWorker('w-test');
|
||||
expect(worker?.beadsCompleted).toBe(2);
|
||||
expect(worker?.beadsReleased).toBe(2);
|
||||
expect(worker?.beadsCompleted).toBe(0); // beadsCompleted only increments on bead.completed
|
||||
});
|
||||
|
||||
it('should clear activeBead and activeFiles on bead.released with release_success', () => {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ const makeWorker = (overrides: Partial<WorkerInfo> = {}): WorkerInfo => ({
|
|||
id: 'w-test',
|
||||
status: 'active',
|
||||
beadsCompleted: 3,
|
||||
beadsReleased: 0,
|
||||
firstSeen: Date.now() - 5 * 60 * 1000,
|
||||
lastActivity: Date.now(),
|
||||
activeFiles: [],
|
||||
|
|
@ -203,6 +204,55 @@ describe('Stuck Detection', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('long-running detection', () => {
|
||||
it('detects worker with many beads released but zero successful completions', () => {
|
||||
// Worker that has processed 100 beads (all timed out/deferred)
|
||||
const worker = makeWorker({
|
||||
firstSeen: Date.now() - 40 * 60 * 1000, // 40 minutes ago
|
||||
lastActivity: Date.now(), // Recent activity to avoid no_progress detection
|
||||
beadsCompleted: 0, // No successful completions
|
||||
beadsReleased: 100, // All beads timed out/deferred
|
||||
});
|
||||
const events: LogEvent[] = [];
|
||||
// Create only 5 events to avoid triggering "events but no completions" in no_progress
|
||||
for (let i = 0; i < 5; i++) {
|
||||
events.push(makeEvent({ ts: Date.now() - i * 30000, msg: 'working on task' }));
|
||||
}
|
||||
|
||||
const pattern = isWorkerStuck(worker, events);
|
||||
|
||||
expect(pattern).not.toBeNull();
|
||||
expect(pattern!.type).toBe('long_running');
|
||||
expect(pattern!.reason).toContain('40m'); // Running for 40 minutes
|
||||
expect(pattern!.reason).toContain('100 processed'); // 100 beads released
|
||||
expect(pattern!.reason).toContain('0 successful completions'); // No successful completions
|
||||
expect(pattern!.reason).toContain('timed out/deferred'); // Clarifies why
|
||||
expect(pattern!.evidence).toContain('Beads successfully completed: 0');
|
||||
expect(pattern!.evidence).toContain('Beads released (including timed-out): 100');
|
||||
});
|
||||
|
||||
it('detects worker with only 1 successful completion after long runtime', () => {
|
||||
const worker = makeWorker({
|
||||
firstSeen: Date.now() - 30 * 60 * 1000, // 30 minutes ago
|
||||
beadsCompleted: 1, // Only 1 successful completion
|
||||
beadsReleased: 50, // 50 beads released (49 timed out)
|
||||
});
|
||||
const events: LogEvent[] = [];
|
||||
for (let i = 0; i < 30; i++) {
|
||||
events.push(makeEvent({ ts: Date.now() - i * 40000 }));
|
||||
}
|
||||
|
||||
const pattern = isWorkerStuck(worker, events);
|
||||
|
||||
expect(pattern).not.toBeNull();
|
||||
expect(pattern!.type).toBe('long_running');
|
||||
expect(pattern!.reason).toContain('30m');
|
||||
expect(pattern!.reason).toContain('only 1 successful completion');
|
||||
expect(pattern!.evidence).toContain('Beads successfully completed: 1');
|
||||
expect(pattern!.evidence).toContain('Beads released (including timed-out): 50');
|
||||
});
|
||||
});
|
||||
|
||||
describe('legacy detection (non-state-transition)', () => {
|
||||
it('still detects repeated tool calls', () => {
|
||||
const worker = makeWorker();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue