Skip to content
Closed
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
18 changes: 16 additions & 2 deletions src/lib/tokenHealthCheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,19 @@ function getConnectionLogLabel(conn: { name?: string; email?: string; id?: strin
return pickMaskedDisplayValue([conn.name, conn.email], conn.id || "-");
}

export function extractResolvedProxyConfig(resolvedProxy: unknown) {
if (
resolvedProxy &&
typeof resolvedProxy === "object" &&
!Array.isArray(resolvedProxy) &&
"proxy" in resolvedProxy
) {
return (resolvedProxy as { proxy?: unknown }).proxy ?? null;
}

return resolvedProxy ?? null;
}

export function buildRefreshFailureUpdate(conn: any, now: string) {
const wasExpired = conn.testStatus === "expired";
const retryCount = (conn.expiredRetryCount ?? 0) + (wasExpired ? 1 : 0);
Expand Down Expand Up @@ -202,7 +215,7 @@ async function sweep() {
/**
* Check a single connection and refresh if due.
*/
async function checkConnection(conn) {
export async function checkConnection(conn) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The conn parameter is implicitly typed as any. Since this is a TypeScript file and the function is now exported for use in tests, it should have a proper type definition to ensure type safety and improve maintainability. Consider defining an interface for the connection object or using a more specific type than the implicit any.

Suggested change
export async function checkConnection(conn) {
export async function checkConnection(conn: any) {

// Determine interval (0 = disabled)
const intervalMin = conn.healthCheckInterval ?? DEFAULT_HEALTH_CHECK_INTERVAL_MIN;
if (intervalMin <= 0) return;
Expand Down Expand Up @@ -255,7 +268,8 @@ async function checkConnection(conn) {
};

const hideLogs = await shouldHideLogs();
const proxyConfig = await resolveProxyForConnection(conn.id);
const proxyResolution = await resolveProxyForConnection(conn.id);
const proxyConfig = extractResolvedProxyConfig(proxyResolution);
const result = await getAccessToken(
conn.provider,
credentials,
Expand Down
236 changes: 236 additions & 0 deletions tests/unit/token-health-check.test.mjs
Original file line number Diff line number Diff line change
@@ -1,10 +1,169 @@
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import http from "node:http";
import net from "node:net";
import os from "node:os";
import path from "node:path";

process.env.NODE_ENV = "test";

const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-token-healthcheck-"));
process.env.DATA_DIR = TEST_DATA_DIR;

const core = await import("../../src/lib/db/core.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const settingsDb = await import("../../src/lib/db/settings.ts");
const { PROVIDERS } = await import("../../open-sse/config/constants.ts");
const tokenHealthCheck = await import("../../src/lib/tokenHealthCheck.ts");

async function resetStorage() {
core.resetDbInstance();

for (let attempt = 0; attempt < 10; attempt++) {
try {
if (fs.existsSync(TEST_DATA_DIR)) {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
}
break;
} catch (error) {
if ((error?.code === "EBUSY" || error?.code === "EPERM") && attempt < 9) {
await new Promise((resolve) => setTimeout(resolve, 50 * (attempt + 1)));
} else {
throw error;
}
}
}

fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}

async function withHttpServer(handler, fn) {
const server = http.createServer(handler);

await new Promise((resolve, reject) => {
server.once("error", reject);
server.listen(0, "127.0.0.1", resolve);
});

const address = server.address();
assert.ok(address && typeof address === "object");

try {
return await fn({
host: "127.0.0.1",
port: address.port,
url: `http://127.0.0.1:${address.port}`,
});
} finally {
await new Promise((resolve, reject) => {
server.close((error) => {
if (error) reject(error);
else resolve();
});
});
}
}

async function withConnectProxyServer(fn) {
const server = http.createServer((_req, res) => {
res.writeHead(501);
res.end("CONNECT only");
});

server.on("connect", (req, clientSocket, head) => {
const [host, portText] = String(req.url || "").split(":");
const targetPort = Number(portText || 80);
const upstreamSocket = net.connect(targetPort, host, () => {
clientSocket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
if (head && head.length > 0) {
upstreamSocket.write(head);
}
upstreamSocket.pipe(clientSocket);
clientSocket.pipe(upstreamSocket);
});

const closeSockets = () => {
upstreamSocket.destroy();
clientSocket.destroy();
};

upstreamSocket.on("error", closeSockets);
clientSocket.on("error", closeSockets);
});

await new Promise((resolve, reject) => {
server.once("error", reject);
server.listen(0, "127.0.0.1", resolve);
});

const address = server.address();
assert.ok(address && typeof address === "object");

try {
return await fn({
host: "127.0.0.1",
port: address.port,
url: `http://127.0.0.1:${address.port}`,
});
} finally {
await new Promise((resolve, reject) => {
server.close((error) => {
if (error) reject(error);
else resolve();
});
});
}
}

async function withPatchedProvider(providerId, config, fn) {
const hadOwnConfig = Object.prototype.hasOwnProperty.call(PROVIDERS, providerId);
const previousConfig = hadOwnConfig ? PROVIDERS[providerId] : undefined;
PROVIDERS[providerId] = config;

try {
return await fn();
} finally {
if (hadOwnConfig) {
PROVIDERS[providerId] = previousConfig;
} else {
delete PROVIDERS[providerId];
}
}
}

test.after(async () => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
});

test("extractResolvedProxyConfig unwraps proxy resolution metadata", () => {
const proxy = {
type: "http",
host: "proxy.example.test",
port: 8080,
};

assert.deepEqual(
tokenHealthCheck.extractResolvedProxyConfig({
proxy,
level: "account",
levelId: "conn-123",
source: "registry",
}),
proxy
);
assert.deepEqual(tokenHealthCheck.extractResolvedProxyConfig(proxy), proxy);
assert.equal(
tokenHealthCheck.extractResolvedProxyConfig({
proxy: null,
level: "direct",
}),
null
);
assert.equal(tokenHealthCheck.extractResolvedProxyConfig(null), null);
});

test("buildRefreshFailureUpdate keeps active connections routable after refresh failure", () => {
const now = "2026-04-09T04:40:00.000Z";

Expand Down Expand Up @@ -41,3 +200,80 @@ test("buildRefreshFailureUpdate preserves expired retry tracking", () => {
assert.equal(update.expiredRetryCount, 3);
assert.equal(update.expiredRetryAt, now);
});

test("checkConnection uses the resolved proxy payload when refreshing tokens", async () => {
await resetStorage();

const providerId = "custom-oauth-healthcheck";
const refreshRequests = [];

await withHttpServer(
(req, res) => {
let body = "";
req.setEncoding("utf8");
req.on("data", (chunk) => {
body += chunk;
});
req.on("end", () => {
refreshRequests.push({
method: req.method,
url: req.url,
headers: req.headers,
body,
});
res.writeHead(200, { "Content-Type": "application/json" });
res.end(
JSON.stringify({
access_token: "new-access-token",
refresh_token: "new-refresh-token",
expires_in: 3600,
})
);
});
},
async (tokenServer) => {
await withConnectProxyServer(async (proxy) => {
await withPatchedProvider(
providerId,
{
tokenUrl: `${tokenServer.url}/token`,
clientId: "healthcheck-client-id",
clientSecret: "healthcheck-client-secret",
},
async () => {
const connection = await providersDb.createProviderConnection({
provider: providerId,
authType: "oauth",
name: "Healthcheck Proxy Account",
email: "healthcheck@example.com",
accessToken: "stale-access-token",
refreshToken: "refresh-token-123",
isActive: true,
});

await settingsDb.setProxyForLevel("key", connection.id, {
type: "http",
host: proxy.host,
port: proxy.port,
});

await tokenHealthCheck.checkConnection(connection);

const updated = await providersDb.getProviderConnectionById(connection.id);

assert.equal(refreshRequests.length, 1);
assert.equal(refreshRequests[0].method, "POST");
assert.equal(refreshRequests[0].url, "/token");
assert.match(refreshRequests[0].body, /grant_type=refresh_token/);
assert.match(refreshRequests[0].body, /refresh_token=refresh-token-123/);
assert.equal(updated?.accessToken, "new-access-token");
assert.equal(updated?.refreshToken, "new-refresh-token");
assert.equal(updated?.testStatus, "active");
assert.equal(updated?.lastError ?? null, null);
assert.ok(updated?.tokenExpiresAt);
}
);
});
}
);
});