Ctrl K
ComunidadGuías…

test/devin-connect-deadline-ref.test.js

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// WHY A CHILD PROCESS. The absolute-deadline timer in streamChat is unref'd by
// default and only enters the loop's ref set while a consumer is blocked in the
// wait (src/devin-connect.js, "consumer wait"). If that ref window is lost, a hung
// upstream can never be ended by the deadline — but the damage is version-gated:
// on Node 20/22 the runtime cancels the pending await ("Promise resolution is
// still pending but the event loop has already resolved") and the in-process test
// at devin-connect.test.js:1147 goes red, while on Node 24 the test runner's own
// handles keep the loop alive and the same defect is invisible. Node 24 is the
// release gate, so the in-process test alone cannot guard this fix.
//
// A standalone process has exactly two things that could keep it alive: the fake
// request (which never responds) and the deadline timer. If the timer is not ref'd
// while the consumer waits, the process exits with an unsettled top-level await
// and status 13 on EVERY version; with the ref window it settles with
// DEADLINE_EXCEEDED and exits 0. That makes this the version-independent guard.
import { spawnSync } from 'node:child_process';
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { dirname, join } from 'node:path';

const root = join(dirname(fileURLToPath(import.meta.url)), '..');
const devinConnect = pathToFileURL(join(root, 'src', 'devin-connect.js')).href;

const script = `
import { EventEmitter } from 'node:events';
const { streamChat, __setRequestImpl } = await import(${JSON.stringify(devinConnect)});
const res = new EventEmitter();
res.statusCode = 200;
// Hung-but-silent upstream: connects, never sends a frame, never errors.
__setRequestImpl((_opts, cb) => {
  const req = new EventEmitter();
  req.destroyed = false;
  req.setTimeout = () => req;      // the idle timer never fires
  req.write = () => {};
  req.destroy = () => { req.destroyed = true; };
  req.end = () => { setTimeout(() => cb(res), 1); };
  return req;
});
try {
  for await (const _event of streamChat({
    messages: [{ role: 'user', content: 'hi' }],
    model: 'swe-1-6-slow',
    token: 'devin-session-token$fake.jwt.sig',
    timeoutMs: 60000,
    deadlineMs: 50,
  })) { /* drain */ }
  console.log('SETTLED:no-error');
} catch (err) {
  console.log('SETTLED:' + err.code);
}
`;

test('a standalone consumer of a hung stream is ended by the absolute deadline, not by loop drain', () => {
  const out = spawnSync(process.execPath, ['--input-type=module', '--eval', script], {
    encoding: 'utf8',
    timeout: 30000,
  });
  assert.equal(
    out.status, 0,
    `child exited ${out.status} (a non-zero status here means the pending await was abandoned by the `
    + `event loop, i.e. the deadline timer was not ref'd while the consumer waited)\n`
    + `stdout: ${out.stdout}\nstderr: ${out.stderr}`,
  );
  assert.match(out.stdout, /SETTLED:DEADLINE_EXCEEDED/, `child did not settle on the deadline: ${out.stdout}`);
});