Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 8 additions & 47 deletions containers/api-proxy/server.anthropic-beta.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@
*/

const https = require('https');
const { EventEmitter } = require('events');
const {
makeReq: makeReqFactory,
makeRes,
makeProxyReq,
makeProxyRes,
getStructuredLogs,
} = require('./test-helpers/server-mock-factories');

const originalHttpsProxy = process.env.HTTPS_PROXY;
let proxyRequest;
Expand Down Expand Up @@ -34,52 +40,7 @@ afterAll(() => {

describe('proxyRequest anthropic deprecated beta handling', () => {
function makeReq(headers = {}) {
const req = new EventEmitter();
req.url = '/v1/messages';
req.method = 'POST';
req.headers = { 'content-type': 'application/json', ...headers };
return req;
}

function makeRes() {
const res = {
headersSent: false,
setHeader: jest.fn(),
writeHead: jest.fn(() => {
res.headersSent = true;
}),
end: jest.fn(),
destroy: jest.fn(),
};
return res;
}

function makeProxyReq() {
const proxyReq = new EventEmitter();
proxyReq.end = jest.fn();
proxyReq.write = jest.fn();
proxyReq.destroy = jest.fn();
return proxyReq;
}

function makeProxyRes(statusCode, headers = { 'content-type': 'application/json' }) {
const proxyRes = new EventEmitter();
proxyRes.statusCode = statusCode;
proxyRes.headers = headers;
proxyRes.pipe = jest.fn();
return proxyRes;
}

function getStructuredLogs(writeSpy, eventName) {
return writeSpy.mock.calls
.map(([line]) => {
try {
return JSON.parse(line);
} catch {
return null;
}
})
.filter(entry => entry && entry.event === eventName);
return makeReqFactory('/v1/messages', headers);
}

afterEach(() => {
Expand Down
34 changes: 7 additions & 27 deletions containers/api-proxy/server.error-handling.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@

const https = require('https');
const { EventEmitter } = require('events');
const {
makeReq: makeReqFactory,
makeRes,
getStructuredLogs,
} = require('./test-helpers/server-mock-factories');

const originalHttpsProxy = process.env.HTTPS_PROXY;
let proxyRequest;
Expand All @@ -29,36 +34,11 @@ afterAll(() => {

describe('proxyRequest error handling', () => {
function makeReq(headers = {}) {
const req = new EventEmitter();
req.url = '/v1/chat/completions';
req.method = 'POST';
req.headers = { 'content-type': 'application/json', ...headers };
return req;
}

function makeRes() {
const res = {
headersSent: false,
setHeader: jest.fn(),
writeHead: jest.fn(() => {
res.headersSent = true;
}),
end: jest.fn(),
destroy: jest.fn(),
};
return res;
return makeReqFactory('/v1/chat/completions', headers);
}

function getRequestErrorLog(writeSpy) {
for (const [line] of writeSpy.mock.calls) {
try {
const parsed = JSON.parse(line);
if (parsed.event === 'request_error') return parsed;
} catch {
// ignore non-JSON writes
}
}
return null;
return getStructuredLogs(writeSpy, 'request_error')[0] || null;
}

let stdoutWriteSpy;
Expand Down
47 changes: 8 additions & 39 deletions containers/api-proxy/server.model-not-supported.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@
*/

const https = require('https');
const { EventEmitter } = require('events');
const {
makeReq: makeReqFactory,
makeRes,
makeProxyReq,
makeProxyRes,
getStructuredLogs,
} = require('./test-helpers/server-mock-factories');

const originalHttpsProxy = process.env.HTTPS_PROXY;
let proxyRequest;
Expand Down Expand Up @@ -36,51 +42,14 @@ afterAll(() => {
// ── helpers ───────────────────────────────────────────────────────────────────

function makeReq(headers = {}) {
const req = new EventEmitter();
req.url = '/v1/chat/completions';
req.method = 'POST';
req.headers = { 'content-type': 'application/json', ...headers };
return req;
}

function makeRes() {
const res = {
headersSent: false,
setHeader: jest.fn(),
writeHead: jest.fn(() => { res.headersSent = true; }),
end: jest.fn(),
destroy: jest.fn(),
};
return res;
}

function makeProxyReq() {
const proxyReq = new EventEmitter();
proxyReq.end = jest.fn();
proxyReq.write = jest.fn();
proxyReq.destroy = jest.fn();
return proxyReq;
}

function makeProxyRes(statusCode, headers = { 'content-type': 'application/json' }) {
const proxyRes = new EventEmitter();
proxyRes.statusCode = statusCode;
proxyRes.headers = headers;
proxyRes.pipe = jest.fn();
return proxyRes;
return makeReqFactory('/v1/chat/completions', headers);
}

/** Flush all pending microtasks/promises so async retry callbacks can run. */
function flushPromises() {
return new Promise(resolve => setImmediate(resolve));
}

function getStructuredLogs(writeSpy, eventName) {
return writeSpy.mock.calls
.map(([line]) => { try { return JSON.parse(line); } catch { return null; } })
.filter(entry => entry && entry.event === eventName);
}

// ── tests ─────────────────────────────────────────────────────────────────────

describe('proxyRequest copilot model-not-supported retry', () => {
Expand Down
27 changes: 7 additions & 20 deletions containers/api-proxy/server.proxy-headers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
*/

const https = require('https');
const { EventEmitter } = require('events');
const {
makeReq: makeReqFactory,
makeRes,
makeProxyReq,
} = require('./test-helpers/server-mock-factories');

const originalHttpsProxy = process.env.HTTPS_PROXY;
let proxyRequest;
Expand All @@ -28,21 +32,7 @@ afterAll(() => {
describe('proxyRequest X-Initiator injection', () => {
/** Minimal mock for http.IncomingMessage backed by EventEmitter. */
function makeReq(headers = {}) {
const req = new EventEmitter();
req.url = '/v1/chat/completions';
req.method = 'POST';
req.headers = { 'content-type': 'application/json', ...headers };
return req;
}

/** Minimal mock for http.ServerResponse. */
function makeRes() {
return {
headersSent: false,
setHeader: jest.fn(),
writeHead: jest.fn(),
end: jest.fn(),
};
return makeReqFactory('/v1/chat/completions', headers);
}

afterEach(() => {
Expand All @@ -58,10 +48,7 @@ describe('proxyRequest X-Initiator injection', () => {
let capturedProxyReq;
jest.spyOn(https, 'request').mockImplementation((options) => {
capturedOptions = options;
const proxyReq = new EventEmitter();
proxyReq.end = jest.fn();
proxyReq.write = jest.fn();
proxyReq.destroy = jest.fn();
const proxyReq = makeProxyReq();
capturedProxyReq = proxyReq;
return proxyReq;
});
Expand Down
31 changes: 3 additions & 28 deletions containers/api-proxy/server.token-guards.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

const https = require('https');
const { EventEmitter } = require('events');
const { makeReq: makeReqFactory, makeRes } = require('./test-helpers/server-mock-factories');

const originalHttpsProxy = process.env.HTTPS_PROXY;
let proxyRequest;
Expand Down Expand Up @@ -34,20 +35,7 @@ afterAll(() => {

describe('proxyRequest effective token guard', () => {
function makeReq(headers = {}) {
const req = new EventEmitter();
req.url = '/v1/chat/completions';
req.method = 'POST';
req.headers = { 'content-type': 'application/json', ...headers };
return req;
}

function makeRes() {
return {
headersSent: false,
setHeader: jest.fn(),
writeHead: jest.fn(),
end: jest.fn(),
};
return makeReqFactory('/v1/chat/completions', headers);
}

beforeEach(() => {
Expand Down Expand Up @@ -113,20 +101,7 @@ describe('proxyRequest effective token guard', () => {

describe('proxyRequest max-runs guard', () => {
function makeReq(headers = {}) {
const req = new EventEmitter();
req.url = '/v1/chat/completions';
req.method = 'POST';
req.headers = { 'content-type': 'application/json', ...headers };
return req;
}

function makeRes() {
return {
headersSent: false,
setHeader: jest.fn(),
writeHead: jest.fn(),
end: jest.fn(),
};
return makeReqFactory('/v1/chat/completions', headers);
}

beforeEach(() => {
Expand Down
54 changes: 54 additions & 0 deletions containers/api-proxy/test-helpers/server-mock-factories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
'use strict';

const { EventEmitter } = require('events');

function makeReq(url, headers = {}) {
const req = new EventEmitter();
req.url = url;
req.method = 'POST';
req.headers = { 'content-type': 'application/json', ...headers };
return req;
}

function makeRes() {
const res = {
headersSent: false,
setHeader: jest.fn(),
writeHead: jest.fn(() => {
res.headersSent = true;
}),
end: jest.fn(),
destroy: jest.fn(),
};
return res;
}

function makeProxyReq() {
const proxyReq = new EventEmitter();
proxyReq.end = jest.fn();
proxyReq.write = jest.fn();
proxyReq.destroy = jest.fn();
return proxyReq;
}

function makeProxyRes(statusCode, headers = { 'content-type': 'application/json' }) {
const proxyRes = new EventEmitter();
proxyRes.statusCode = statusCode;
proxyRes.headers = headers;
proxyRes.pipe = jest.fn();
return proxyRes;
}

function getStructuredLogs(writeSpy, eventName) {
return writeSpy.mock.calls
.map(([line]) => {
try {
return JSON.parse(line);
} catch {
return null;
}
})
.filter(entry => entry && entry.event === eventName);
}

module.exports = { makeReq, makeRes, makeProxyReq, makeProxyRes, getStructuredLogs };
Loading