Files
EJClaw/src/db-paired-turn-attempts-bad-request.test.ts
2026-04-29 22:20:06 +09:00

156 lines
3.8 KiB
TypeScript

import { beforeEach, describe, expect, it } from 'vitest';
import {
_initTestDatabase,
createPairedTask,
failPairedTurn,
getOwnerCodexBadRequestFailureSummaryForTask,
markPairedTurnRunning,
} from './db.js';
import { CODEX_MAIN_SERVICE_ID } from './config.js';
import { buildPairedTurnIdentity } from './paired-turn-identity.js';
import type { PairedTask } from './types.js';
const BAD_REQUEST_ERROR = '{"detail":"Bad Request"}';
beforeEach(() => {
_initTestDatabase();
});
function makeTask(overrides: Partial<PairedTask> = {}): PairedTask {
return {
id: 'task-bad-request',
chat_jid: 'dc:room',
group_folder: 'eyejokerdb-9',
owner_service_id: CODEX_MAIN_SERVICE_ID,
reviewer_service_id: 'claude',
title: null,
source_ref: null,
plan_notes: null,
review_requested_at: null,
round_trip_count: 1,
status: 'active',
arbiter_verdict: null,
arbiter_requested_at: null,
completion_reason: null,
created_at: '2026-04-29T08:00:00.000Z',
updated_at: '2026-04-29T08:00:00.000Z',
...overrides,
};
}
function recordOwnerCodexFailure(args: {
taskId: string;
taskUpdatedAt: string;
runId: string;
error: string;
}): void {
const turnIdentity = buildPairedTurnIdentity({
taskId: args.taskId,
taskUpdatedAt: args.taskUpdatedAt,
intentKind: 'owner-follow-up',
role: 'owner',
});
markPairedTurnRunning({
turnIdentity,
executorServiceId: CODEX_MAIN_SERVICE_ID,
executorAgentType: 'codex',
runId: args.runId,
});
failPairedTurn({ turnIdentity, error: args.error });
}
describe('owner Codex Bad Request attempt summaries', () => {
it('summarizes consecutive owner Codex Bad Request failures for a task', () => {
const task = makeTask();
createPairedTask(task);
for (const runId of ['run-1', 'run-2', 'run-3']) {
recordOwnerCodexFailure({
taskId: task.id,
taskUpdatedAt: task.updated_at,
runId,
error: BAD_REQUEST_ERROR,
});
}
expect(
getOwnerCodexBadRequestFailureSummaryForTask({
taskId: task.id,
threshold: 3,
}),
).toMatchObject({
taskId: task.id,
failures: 3,
});
});
it('does not summarize when the latest owner Codex failures are not all the narrow Bad Request signal', () => {
const task = makeTask();
createPairedTask(task);
recordOwnerCodexFailure({
taskId: task.id,
taskUpdatedAt: task.updated_at,
runId: 'run-1',
error: BAD_REQUEST_ERROR,
});
recordOwnerCodexFailure({
taskId: task.id,
taskUpdatedAt: task.updated_at,
runId: 'run-2',
error: BAD_REQUEST_ERROR,
});
recordOwnerCodexFailure({
taskId: task.id,
taskUpdatedAt: task.updated_at,
runId: 'run-3',
error: 'HTTP 400 Bad Request',
});
expect(
getOwnerCodexBadRequestFailureSummaryForTask({
taskId: task.id,
threshold: 3,
}),
).toBeNull();
});
it('counts only the latest consecutive Bad Request failures', () => {
const task = makeTask();
createPairedTask(task);
recordOwnerCodexFailure({
taskId: task.id,
taskUpdatedAt: task.updated_at,
runId: 'run-1',
error: BAD_REQUEST_ERROR,
});
recordOwnerCodexFailure({
taskId: task.id,
taskUpdatedAt: task.updated_at,
runId: 'run-2',
error: 'HTTP 400 Bad Request',
});
for (const runId of ['run-3', 'run-4', 'run-5']) {
recordOwnerCodexFailure({
taskId: task.id,
taskUpdatedAt: task.updated_at,
runId,
error: BAD_REQUEST_ERROR,
});
}
expect(
getOwnerCodexBadRequestFailureSummaryForTask({
taskId: task.id,
threshold: 3,
}),
).toMatchObject({
taskId: task.id,
failures: 3,
});
});
});