mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-26 00:52:18 +03:00
feat(dashboard): surface exclusive managed leases in Sessions view
Reuse the official exclusive-lease authority for durable idle visibility, and use pending-request accounting only for the localized active indication. Preserve legacy session fields and rows while de-duplicating leased connections and withholding lease ownership and fencing data. Refs #10514 Follow-up to #10362
This commit is contained in:
@@ -0,0 +1 @@
|
||||
- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse
|
||||
@@ -2,19 +2,47 @@
|
||||
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
mergeDashboardSessions,
|
||||
type DashboardSession,
|
||||
type ExclusiveDashboardSession,
|
||||
type RecentSessionForDashboard,
|
||||
} from "@/lib/sessionObservability";
|
||||
import { Card } from "@/shared/components";
|
||||
|
||||
type SessionsResponse = {
|
||||
sessions: RecentSessionForDashboard[];
|
||||
exclusiveSessions: ExclusiveDashboardSession[];
|
||||
};
|
||||
|
||||
const EMPTY_DATA: SessionsResponse = {
|
||||
sessions: [],
|
||||
exclusiveSessions: [],
|
||||
};
|
||||
|
||||
function isLeaseBackedSession(session: DashboardSession): session is ExclusiveDashboardSession {
|
||||
return "leaseBacked" in session && session.leaseBacked;
|
||||
}
|
||||
|
||||
export default function SessionsTab() {
|
||||
const t = useTranslations("usage");
|
||||
const [data, setData] = useState({ count: 0, sessions: [] });
|
||||
const tCommon = useTranslations("common");
|
||||
const [data, setData] = useState<SessionsResponse>(EMPTY_DATA);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const loadSessions = useCallback(async () => {
|
||||
try {
|
||||
const res = await fetch("/api/sessions");
|
||||
if (res.ok) setData(await res.json());
|
||||
if (res.ok) {
|
||||
const next = await res.json();
|
||||
setData({
|
||||
sessions: Array.isArray(next.sessions) ? next.sessions : [],
|
||||
exclusiveSessions: Array.isArray(next.exclusiveSessions) ? next.exclusiveSessions : [],
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
// A failed background poll leaves the last successful Sessions snapshot visible.
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -26,7 +54,12 @@ export default function SessionsTab() {
|
||||
return () => clearInterval(interval);
|
||||
}, [loadSessions]);
|
||||
|
||||
const formatAge = (ms) => {
|
||||
const displaySessions = useMemo(() => {
|
||||
return mergeDashboardSessions(data.exclusiveSessions, data.sessions);
|
||||
}, [data.exclusiveSessions, data.sessions]);
|
||||
|
||||
const formatAge = (ms: number | null) => {
|
||||
if (ms == null) return t("notAvailableSymbol");
|
||||
if (ms < 60000) return t("durationSecondsShort", { value: Math.floor(ms / 1000) });
|
||||
if (ms < 3600000) return t("durationMinutesShort", { value: Math.floor(ms / 60000) });
|
||||
return t("durationHoursShort", { value: Math.floor(ms / 3600000) });
|
||||
@@ -47,12 +80,17 @@ export default function SessionsTab() {
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="flex items-center gap-1.5 px-3 py-1.5 rounded-full bg-cyan-500/10 border border-cyan-500/20">
|
||||
<span className="w-2 h-2 rounded-full bg-cyan-500 animate-pulse" />
|
||||
<span className="text-sm font-semibold tabular-nums text-cyan-400">{data.count}</span>
|
||||
<span
|
||||
className="text-sm font-semibold tabular-nums text-cyan-400"
|
||||
data-testid="session-count"
|
||||
>
|
||||
{displaySessions.length}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{data.sessions.length === 0 ? (
|
||||
{displaySessions.length === 0 ? (
|
||||
<div className="text-center py-8 text-text-muted">
|
||||
<span
|
||||
className="material-symbols-outlined text-[40px] mb-2 block opacity-40"
|
||||
@@ -83,31 +121,46 @@ export default function SessionsTab() {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.sessions.map((s) => (
|
||||
<tr
|
||||
key={s.sessionId}
|
||||
className="border-b border-border/10 hover:bg-surface/20 transition-colors"
|
||||
>
|
||||
<td className="py-2.5 px-3">
|
||||
<span className="font-mono text-xs px-2 py-1 rounded bg-surface/40 text-text-muted">
|
||||
{s.sessionId.slice(0, 12)}…
|
||||
</span>
|
||||
</td>
|
||||
<td className="py-2.5 px-3 text-text-muted tabular-nums">{formatAge(s.ageMs)}</td>
|
||||
<td className="py-2.5 px-3 text-right">
|
||||
<span className="font-semibold tabular-nums">{s.requestCount}</span>
|
||||
</td>
|
||||
<td className="py-2.5 px-3">
|
||||
{s.connectionId ? (
|
||||
<span className="text-xs font-mono text-cyan-400">
|
||||
{s.connectionId.slice(0, 10)}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-text-muted/40">{t("notAvailableSymbol")}</span>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{displaySessions.map((s) => {
|
||||
const leaseBacked = isLeaseBackedSession(s);
|
||||
return (
|
||||
<tr
|
||||
key={s.sessionId}
|
||||
className="border-b border-border/10 hover:bg-surface/20 transition-colors"
|
||||
>
|
||||
<td className="py-2.5 px-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<span
|
||||
className="font-mono text-xs px-2 py-1 rounded bg-surface/40 text-text-muted"
|
||||
title={s.sessionId}
|
||||
>
|
||||
{s.sessionId.slice(0, 12)}…
|
||||
</span>
|
||||
{leaseBacked && s.active && (
|
||||
<span className="text-[10px] font-semibold tracking-wide px-2 py-0.5 rounded-full border text-green-400 border-green-500/30 bg-green-500/10">
|
||||
{tCommon("active")}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="py-2.5 px-3 text-text-muted tabular-nums">
|
||||
{formatAge(s.ageMs)}
|
||||
</td>
|
||||
<td className="py-2.5 px-3 text-right">
|
||||
<span className="font-semibold tabular-nums">{s.requestCount}</span>
|
||||
</td>
|
||||
<td className="py-2.5 px-3">
|
||||
{s.connectionId ? (
|
||||
<span className="text-xs font-mono text-cyan-400" title={s.connectionId}>
|
||||
{(leaseBacked && s.connectionName) || s.connectionId.slice(0, 10)}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-text-muted/40">{t("notAvailableSymbol")}</span>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -5,13 +5,44 @@ import {
|
||||
getAllActiveSessionCountsByKey,
|
||||
} from "@omniroute/open-sse/services/sessionManager.ts";
|
||||
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error";
|
||||
import { getExclusiveLeaseConnectionIds } from "@/lib/db/apiKeys";
|
||||
import { getExclusiveLeaseOccupancy } from "@/lib/db/exclusiveConnectionLeases";
|
||||
import { getProviderConnectionDisplayMetadata } from "@/lib/db/providers";
|
||||
import { getAccountDisplayName } from "@/lib/display/names";
|
||||
import { getPendingRequests } from "@/lib/usage/usageHistory";
|
||||
import { buildExclusiveDashboardSessions } from "@/lib/sessionObservability";
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const sessions = getActiveSessions();
|
||||
const count = getActiveSessionCount();
|
||||
const byApiKey = getAllActiveSessionCountsByKey();
|
||||
return NextResponse.json({ count, sessions, byApiKey });
|
||||
|
||||
// Reuse the hard-lease authority added by #10362. The API-key policy derives
|
||||
// the managed candidate set; SQLite occupancy is the source of truth for
|
||||
// which of those connections are actually leased right now.
|
||||
const managedConnectionIds = Array.from(await getExclusiveLeaseConnectionIds());
|
||||
const occupancy = getExclusiveLeaseOccupancy(managedConnectionIds);
|
||||
const leasedConnectionIds = new Set(occupancy.keys());
|
||||
const connectionNames = new Map(
|
||||
getProviderConnectionDisplayMetadata([...leasedConnectionIds]).map((connection) => [
|
||||
connection.id,
|
||||
getAccountDisplayName(connection),
|
||||
])
|
||||
);
|
||||
const exclusiveSessions = buildExclusiveDashboardSessions(
|
||||
leasedConnectionIds,
|
||||
getPendingRequests().byAccount,
|
||||
sessions,
|
||||
connectionNames
|
||||
);
|
||||
|
||||
return NextResponse.json({
|
||||
count,
|
||||
sessions,
|
||||
byApiKey,
|
||||
exclusiveSessions,
|
||||
});
|
||||
} catch (error) {
|
||||
return NextResponse.json({ error: sanitizeErrorMessage(error) }, { status: 500 });
|
||||
}
|
||||
|
||||
@@ -344,6 +344,43 @@ export async function getProviderConnectionById(id: string) {
|
||||
);
|
||||
}
|
||||
|
||||
export interface ProviderConnectionDisplayMetadata {
|
||||
id: string;
|
||||
name: string | null;
|
||||
displayName: string | null;
|
||||
email: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads only the non-credential fields needed by account display-name resolvers.
|
||||
*
|
||||
* This avoids decrypting provider credentials when a dashboard only needs labels.
|
||||
*/
|
||||
export function getProviderConnectionDisplayMetadata(
|
||||
connectionIds: readonly string[]
|
||||
): ProviderConnectionDisplayMetadata[] {
|
||||
const ids = [...new Set(connectionIds.filter((id) => id.length > 0))];
|
||||
if (ids.length === 0) return [];
|
||||
|
||||
const db = getDbInstance() as unknown as DbLike;
|
||||
const rows = db
|
||||
.prepare(
|
||||
`SELECT id, name, display_name, email FROM provider_connections
|
||||
WHERE id IN (${ids.map(() => "?").join(", ")})`
|
||||
)
|
||||
.all(...ids);
|
||||
|
||||
return rows.map((row) => {
|
||||
const view = rowToCamel(row) as JsonRecord;
|
||||
return {
|
||||
id: toStringOrNull(view.id) || "",
|
||||
name: toStringOrNull(view.name),
|
||||
displayName: toStringOrNull(view.displayName),
|
||||
email: toStringOrNull(view.email),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// #3368 PR6 — dedup web-session cookie/token credentials on connection create.
|
||||
// Re-importing the same session (e.g. via bulk web-session import) under a
|
||||
// different or blank name must update the existing connection instead of
|
||||
|
||||
93
src/lib/sessionObservability.ts
Normal file
93
src/lib/sessionObservability.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
export type RecentSessionForDashboard = {
|
||||
sessionId: string;
|
||||
ageMs: number;
|
||||
requestCount: number;
|
||||
connectionId: string | null;
|
||||
};
|
||||
|
||||
export type PendingRequestsByAccount = Record<string, Record<string, number>>;
|
||||
|
||||
export type ExclusiveDashboardSession = {
|
||||
sessionId: string;
|
||||
ageMs: null;
|
||||
requestCount: number;
|
||||
connectionId: string;
|
||||
connectionName: string | null;
|
||||
leaseBacked: true;
|
||||
active: boolean;
|
||||
};
|
||||
|
||||
export type DashboardSession = RecentSessionForDashboard | ExclusiveDashboardSession;
|
||||
|
||||
function positiveCount(value: unknown): number {
|
||||
const count = Number(value);
|
||||
return Number.isFinite(count) && count > 0 ? count : 0;
|
||||
}
|
||||
|
||||
function countInFlightRequests(
|
||||
pendingByAccount: PendingRequestsByAccount,
|
||||
connectionId: string
|
||||
): number {
|
||||
return Object.values(pendingByAccount[connectionId] ?? {}).reduce(
|
||||
(total, count) => total + positiveCount(count),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the dashboard-only view of durable exclusive leases.
|
||||
*
|
||||
* The lease table remains the lifecycle authority. The request tracker is used
|
||||
* only to flag work currently in flight for an already-held lease; it never
|
||||
* creates, extends, or removes lease ownership.
|
||||
*
|
||||
* Deliberately does not expose the persisted owner hash, API-key id, or lease
|
||||
* generation. The dashboard needs occupancy, connection binding, and activity
|
||||
* state — not fencing material.
|
||||
*/
|
||||
export function buildExclusiveDashboardSessions(
|
||||
leasedConnectionIds: ReadonlySet<string>,
|
||||
pendingByAccount: PendingRequestsByAccount,
|
||||
recentSessions: readonly RecentSessionForDashboard[],
|
||||
connectionNames: ReadonlyMap<string, string> = new Map()
|
||||
): ExclusiveDashboardSession[] {
|
||||
const recentRequestsByConnection = new Map<string, number>();
|
||||
for (const session of recentSessions) {
|
||||
if (!session.connectionId) continue;
|
||||
recentRequestsByConnection.set(
|
||||
session.connectionId,
|
||||
(recentRequestsByConnection.get(session.connectionId) ?? 0) +
|
||||
positiveCount(session.requestCount)
|
||||
);
|
||||
}
|
||||
|
||||
return Array.from(leasedConnectionIds)
|
||||
.map((connectionId) => ({
|
||||
sessionId: `lease:${connectionId}`,
|
||||
ageMs: null,
|
||||
requestCount: recentRequestsByConnection.get(connectionId) ?? 0,
|
||||
connectionId,
|
||||
connectionName: connectionNames.get(connectionId) ?? null,
|
||||
leaseBacked: true as const,
|
||||
active: countInFlightRequests(pendingByAccount, connectionId) > 0,
|
||||
}))
|
||||
.sort((left, right) => {
|
||||
if (left.active !== right.active) return left.active ? -1 : 1;
|
||||
return left.connectionId.localeCompare(right.connectionId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Lease-backed rows replace request-derived rows for the same connection.
|
||||
* Sessions without a connection binding remain untouched.
|
||||
*/
|
||||
export function mergeDashboardSessions(
|
||||
leaseSessions: readonly ExclusiveDashboardSession[],
|
||||
recentSessions: readonly RecentSessionForDashboard[]
|
||||
): DashboardSession[] {
|
||||
const leasedConnectionIds = new Set(leaseSessions.map((session) => session.connectionId));
|
||||
const unleasedRecentSessions = recentSessions.filter(
|
||||
(session) => !session.connectionId || !leasedConnectionIds.has(session.connectionId)
|
||||
);
|
||||
return [...leaseSessions, ...unleasedRecentSessions];
|
||||
}
|
||||
332
tests/unit/exclusive-session-observability.test.ts
Normal file
332
tests/unit/exclusive-session-observability.test.ts
Normal file
@@ -0,0 +1,332 @@
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import test from "node:test";
|
||||
|
||||
import {
|
||||
buildExclusiveDashboardSessions,
|
||||
mergeDashboardSessions,
|
||||
} from "../../src/lib/sessionObservability.ts";
|
||||
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-session-observability-"));
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
process.env.DISABLE_SQLITE_AUTO_BACKUP = "true";
|
||||
process.env.API_KEY_SECRET = "ab".repeat(32);
|
||||
|
||||
const core = await import("../../src/lib/db/core.ts");
|
||||
const apiKeys = await import("../../src/lib/db/apiKeys.ts");
|
||||
const leases = await import("../../src/lib/db/exclusiveConnectionLeases.ts");
|
||||
const providers = await import("../../src/lib/db/providers.ts");
|
||||
const sessionManager = await import("../../open-sse/services/sessionManager.ts");
|
||||
const usageHistory = await import("../../src/lib/usage/usageHistory.ts");
|
||||
const sessionsRoute = await import("../../src/app/api/sessions/route.ts");
|
||||
|
||||
const OWNER_A = "vlo_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
const OWNER_B = "vlo_BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
||||
const BASE_TIME = Date.parse("2026-08-24T12:00:00.000Z");
|
||||
|
||||
function at(offsetMs: number): string {
|
||||
return new Date(BASE_TIME + offsetMs).toISOString();
|
||||
}
|
||||
|
||||
function projectOfficialOccupancy(connectionIds: string[], now: string) {
|
||||
const occupancy = leases.getExclusiveLeaseOccupancy(connectionIds, now);
|
||||
return buildExclusiveDashboardSessions(new Set(occupancy.keys()), {}, []);
|
||||
}
|
||||
|
||||
test.afterEach(() => {
|
||||
sessionManager.clearSessions();
|
||||
usageHistory.clearPendingRequests();
|
||||
});
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
test("projects idle and active leases, distinct connections, legacy rows, and de-duplication", () => {
|
||||
const leaseRows = buildExclusiveDashboardSessions(
|
||||
new Set(["conn-idle", "conn-active"]),
|
||||
{ "conn-active": { "gpt-5.6-sol (codex)": 2 }, "conn-idle": { ignored: 0 } },
|
||||
[
|
||||
{
|
||||
sessionId: "legacy-duplicate-a",
|
||||
ageMs: 10_000,
|
||||
requestCount: 2,
|
||||
connectionId: "conn-active",
|
||||
},
|
||||
{
|
||||
sessionId: "legacy-duplicate-b",
|
||||
ageMs: 5_000,
|
||||
requestCount: 3,
|
||||
connectionId: "conn-active",
|
||||
},
|
||||
],
|
||||
new Map([
|
||||
["conn-active", "Managed Active"],
|
||||
["conn-idle", "Managed Idle"],
|
||||
])
|
||||
);
|
||||
|
||||
assert.equal(leaseRows.length, 2);
|
||||
assert.deepEqual(
|
||||
leaseRows.map((row) => [row.connectionId, row.active, row.requestCount]),
|
||||
[
|
||||
["conn-active", true, 5],
|
||||
["conn-idle", false, 0],
|
||||
]
|
||||
);
|
||||
assert.equal(leaseRows[0].connectionName, "Managed Active");
|
||||
|
||||
const displayed = mergeDashboardSessions(leaseRows, [
|
||||
{
|
||||
sessionId: "legacy-duplicate-a",
|
||||
ageMs: 10_000,
|
||||
requestCount: 2,
|
||||
connectionId: "conn-active",
|
||||
},
|
||||
{
|
||||
sessionId: "legacy-unmanaged",
|
||||
ageMs: 2_000,
|
||||
requestCount: 1,
|
||||
connectionId: "conn-unmanaged",
|
||||
},
|
||||
{
|
||||
sessionId: "legacy-unbound",
|
||||
ageMs: 1_000,
|
||||
requestCount: 1,
|
||||
connectionId: null,
|
||||
},
|
||||
]);
|
||||
|
||||
assert.deepEqual(
|
||||
displayed.map((row) => row.sessionId),
|
||||
["lease:conn-active", "lease:conn-idle", "legacy-unmanaged", "legacy-unbound"]
|
||||
);
|
||||
});
|
||||
|
||||
test("lease projection is a minimum privacy-safe observability payload", () => {
|
||||
const secretOwnerHash = "c".repeat(64);
|
||||
const rows = buildExclusiveDashboardSessions(
|
||||
new Set(["conn-private"]),
|
||||
{},
|
||||
[],
|
||||
new Map([["conn-private", "Private account"]])
|
||||
);
|
||||
const payload = JSON.stringify(rows);
|
||||
|
||||
assert.deepEqual(Object.keys(rows[0]).sort(), [
|
||||
"active",
|
||||
"ageMs",
|
||||
"connectionId",
|
||||
"connectionName",
|
||||
"leaseBacked",
|
||||
"requestCount",
|
||||
"sessionId",
|
||||
]);
|
||||
for (const forbidden of [
|
||||
secretOwnerHash,
|
||||
"leaseOwnerHash",
|
||||
"lease_owner_hash",
|
||||
"generation",
|
||||
"apiKeyId",
|
||||
"leaseOwnerId",
|
||||
"expiresAt",
|
||||
"IDLE",
|
||||
]) {
|
||||
assert.equal(payload.includes(forbidden), false, `payload must not contain ${forbidden}`);
|
||||
}
|
||||
});
|
||||
|
||||
test("official SQLite lease lifecycle remains visible through idle renew, release, and expiry", async () => {
|
||||
const connectionA = "11111111-1111-4111-8111-111111111111";
|
||||
const connectionB = "22222222-2222-4222-8222-222222222222";
|
||||
const managedKey = await apiKeys.createApiKey(
|
||||
"Lifecycle managed key",
|
||||
"0123456789abcdef",
|
||||
["lease:exclusive"],
|
||||
{ allowedConnections: [connectionA, connectionB] }
|
||||
);
|
||||
const managed = await apiKeys.getExclusiveLeaseConnectionIds();
|
||||
assert.equal(managed.has(connectionA), true);
|
||||
assert.equal(managed.has(connectionB), true);
|
||||
|
||||
const acquired = leases.acquireExclusiveConnectionLease({
|
||||
leaseOwnerId: OWNER_A,
|
||||
apiKeyId: managedKey.id,
|
||||
provider: "codex",
|
||||
connectionId: connectionA,
|
||||
now: at(0),
|
||||
ttlMs: 120_000,
|
||||
});
|
||||
assert.equal(acquired.kind, "ACQUIRED");
|
||||
if (acquired.kind !== "ACQUIRED") return;
|
||||
assert.equal(projectOfficialOccupancy([connectionA], at(30_000)).length, 1);
|
||||
assert.equal(projectOfficialOccupancy([connectionA], at(30_000))[0].active, false);
|
||||
|
||||
const renewed = leases.renewExclusiveConnectionLease({
|
||||
leaseOwnerId: OWNER_A,
|
||||
generation: acquired.lease.generation,
|
||||
apiKeyId: managedKey.id,
|
||||
now: at(60_000),
|
||||
ttlMs: 120_000,
|
||||
});
|
||||
assert.equal(renewed.kind, "RENEWED");
|
||||
if (renewed.kind !== "RENEWED") return;
|
||||
assert.equal(renewed.lease.generation, acquired.lease.generation);
|
||||
assert.equal(projectOfficialOccupancy([connectionA], at(150_000)).length, 1);
|
||||
assert.equal(
|
||||
leases.assertExclusiveConnectionLeaseFence({
|
||||
leaseOwnerId: OWNER_A,
|
||||
generation: acquired.lease.generation,
|
||||
apiKeyId: managedKey.id,
|
||||
connectionId: connectionA,
|
||||
now: at(150_000),
|
||||
}).kind,
|
||||
"VALID"
|
||||
);
|
||||
assert.equal(
|
||||
leases.releaseExclusiveConnectionLease({
|
||||
leaseOwnerId: OWNER_A,
|
||||
generation: acquired.lease.generation + 1,
|
||||
apiKeyId: managedKey.id,
|
||||
now: at(151_000),
|
||||
}).kind,
|
||||
"STALE"
|
||||
);
|
||||
assert.equal(projectOfficialOccupancy([connectionA], at(152_000)).length, 1);
|
||||
|
||||
assert.equal(
|
||||
leases.releaseExclusiveConnectionLease({
|
||||
leaseOwnerId: OWNER_A,
|
||||
generation: acquired.lease.generation,
|
||||
apiKeyId: managedKey.id,
|
||||
now: at(153_000),
|
||||
}).kind,
|
||||
"RELEASED"
|
||||
);
|
||||
assert.equal(projectOfficialOccupancy([connectionA], at(154_000)).length, 0);
|
||||
|
||||
const expiring = leases.acquireExclusiveConnectionLease({
|
||||
leaseOwnerId: OWNER_B,
|
||||
apiKeyId: managedKey.id,
|
||||
provider: "codex",
|
||||
connectionId: connectionB,
|
||||
now: at(200_000),
|
||||
ttlMs: 1_000,
|
||||
});
|
||||
assert.equal(expiring.kind, "ACQUIRED");
|
||||
if (expiring.kind !== "ACQUIRED") return;
|
||||
assert.equal(projectOfficialOccupancy([connectionB], at(200_500)).length, 1);
|
||||
assert.equal(leases.reconcileExpiredExclusiveConnectionLeases(at(202_000)), 1);
|
||||
assert.equal(projectOfficialOccupancy([connectionB], at(202_000)).length, 0);
|
||||
|
||||
const reacquired = leases.acquireExclusiveConnectionLease({
|
||||
leaseOwnerId: OWNER_B,
|
||||
apiKeyId: managedKey.id,
|
||||
provider: "codex",
|
||||
connectionId: connectionB,
|
||||
now: at(203_000),
|
||||
});
|
||||
assert.equal(reacquired.kind, "ACQUIRED");
|
||||
if (reacquired.kind !== "ACQUIRED") return;
|
||||
assert.equal(reacquired.lease.generation, expiring.lease.generation + 1);
|
||||
assert.equal(
|
||||
leases.assertExclusiveConnectionLeaseFence({
|
||||
leaseOwnerId: OWNER_B,
|
||||
generation: expiring.lease.generation,
|
||||
apiKeyId: managedKey.id,
|
||||
connectionId: connectionB,
|
||||
now: at(204_000),
|
||||
}).kind,
|
||||
"STALE"
|
||||
);
|
||||
assert.equal(
|
||||
leases.releaseExclusiveConnectionLease({
|
||||
leaseOwnerId: OWNER_B,
|
||||
generation: reacquired.lease.generation,
|
||||
apiKeyId: managedKey.id,
|
||||
now: at(205_000),
|
||||
}).kind,
|
||||
"RELEASED"
|
||||
);
|
||||
});
|
||||
|
||||
test("sessions API keeps legacy fields additive and decorates only in-flight leased work", async () => {
|
||||
const connection = await providers.createProviderConnection({
|
||||
provider: "codex",
|
||||
authType: "oauth",
|
||||
name: "Friendly Lease Account",
|
||||
accessToken: "synthetic-local-token",
|
||||
});
|
||||
const managedKey = await apiKeys.createApiKey(
|
||||
"Route managed key",
|
||||
"fedcba9876543210",
|
||||
["lease:exclusive"],
|
||||
{ allowedConnections: [connection.id] }
|
||||
);
|
||||
const acquired = leases.acquireExclusiveConnectionLease({
|
||||
leaseOwnerId: "vlo_CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
|
||||
apiKeyId: managedKey.id,
|
||||
provider: "codex",
|
||||
connectionId: connection.id,
|
||||
});
|
||||
assert.equal(acquired.kind, "ACQUIRED");
|
||||
if (acquired.kind !== "ACQUIRED") return;
|
||||
|
||||
sessionManager.touchSession("legacy-unmanaged", "legacy-connection");
|
||||
const idleResponse = await sessionsRoute.GET();
|
||||
const idleBody = (await idleResponse.json()) as Record<string, unknown>;
|
||||
assert.equal(idleResponse.status, 200);
|
||||
assert.equal(idleBody.count, 1);
|
||||
assert.equal(Array.isArray(idleBody.sessions), true);
|
||||
assert.equal(
|
||||
(idleBody.sessions as Array<{ sessionId: string }>)[0].sessionId,
|
||||
"legacy-unmanaged"
|
||||
);
|
||||
assert.deepEqual(idleBody.byApiKey, {});
|
||||
const idleLease = (idleBody.exclusiveSessions as Array<Record<string, unknown>>)[0];
|
||||
assert.equal(idleLease.connectionId, connection.id);
|
||||
assert.equal(idleLease.connectionName, "Friendly Lease Account");
|
||||
assert.equal(idleLease.active, false);
|
||||
for (const forbidden of ["leaseOwnerHash", "lease_owner_hash", "generation", "apiKeyId"]) {
|
||||
assert.equal(JSON.stringify(idleBody).includes(forbidden), false);
|
||||
}
|
||||
|
||||
usageHistory.trackPendingRequest("gpt-5.6-sol", "codex", connection.id, true);
|
||||
const activeBody = (await (await sessionsRoute.GET()).json()) as {
|
||||
exclusiveSessions: Array<{ active: boolean }>;
|
||||
};
|
||||
assert.equal(activeBody.exclusiveSessions[0].active, true);
|
||||
usageHistory.trackPendingRequest("gpt-5.6-sol", "codex", connection.id, false);
|
||||
|
||||
assert.equal(
|
||||
leases.releaseExclusiveConnectionLease({
|
||||
leaseOwnerId: "vlo_CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
|
||||
generation: acquired.lease.generation,
|
||||
apiKeyId: managedKey.id,
|
||||
}).kind,
|
||||
"RELEASED"
|
||||
);
|
||||
const releasedBody = (await (await sessionsRoute.GET()).json()) as {
|
||||
count: number;
|
||||
sessions: Array<{ sessionId: string }>;
|
||||
exclusiveSessions: unknown[];
|
||||
};
|
||||
assert.equal(releasedBody.count, 1);
|
||||
assert.equal(releasedBody.sessions[0].sessionId, "legacy-unmanaged");
|
||||
assert.deepEqual(releasedBody.exclusiveSessions, []);
|
||||
});
|
||||
|
||||
test("sessions route keeps raw lease SQL out of the API and sanitizes failures", () => {
|
||||
const route = fs.readFileSync(
|
||||
new URL("../../src/app/api/sessions/route.ts", import.meta.url),
|
||||
"utf8"
|
||||
);
|
||||
assert.match(route, /getExclusiveLeaseConnectionIds/);
|
||||
assert.match(route, /getExclusiveLeaseOccupancy/);
|
||||
assert.match(route, /getPendingRequests/);
|
||||
assert.match(route, /sanitizeErrorMessage\(error\)/);
|
||||
assert.doesNotMatch(route, /SELECT\s|exclusive_connection_leases/i);
|
||||
});
|
||||
139
tests/unit/ui/exclusive-session-observability-ui.test.tsx
Normal file
139
tests/unit/ui/exclusive-session-observability-ui.test.tsx
Normal file
@@ -0,0 +1,139 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import React, { act } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { afterEach, beforeEach, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("next-intl", () => ({
|
||||
useTranslations: (namespace: string) => (key: string) => {
|
||||
if (namespace === "common" && key === "active") return "Localized active";
|
||||
if (namespace === "usage" && key === "noSessions") return "Localized empty state";
|
||||
return key;
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("@/shared/components", () => ({
|
||||
Card: ({ children }: { children: React.ReactNode }) => <div data-testid="card">{children}</div>,
|
||||
}));
|
||||
|
||||
const { default: SessionsTab } =
|
||||
await import("../../../src/app/(dashboard)/dashboard/usage/components/SessionsTab");
|
||||
|
||||
let container: HTMLDivElement;
|
||||
let root: ReturnType<typeof createRoot>;
|
||||
|
||||
beforeEach(() => {
|
||||
(
|
||||
globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }
|
||||
).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
container = document.createElement("div");
|
||||
document.body.appendChild(container);
|
||||
root = createRoot(container);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
act(() => root.unmount());
|
||||
container.remove();
|
||||
vi.unstubAllGlobals();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
async function renderPayload(payload: Record<string, unknown>): Promise<void> {
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(async () => ({
|
||||
ok: true,
|
||||
json: async () => payload,
|
||||
}))
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
root.render(<SessionsTab />);
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
it("keeps idle leases visible, de-duplicates legacy rows, and localizes only active work", async () => {
|
||||
await renderPayload({
|
||||
count: 2,
|
||||
sessions: [
|
||||
{
|
||||
sessionId: "legacy-active",
|
||||
ageMs: 1_000,
|
||||
requestCount: 4,
|
||||
connectionId: "conn-active",
|
||||
},
|
||||
{
|
||||
sessionId: "legacy-unmanaged",
|
||||
ageMs: 2_000,
|
||||
requestCount: 1,
|
||||
connectionId: "conn-unmanaged",
|
||||
},
|
||||
],
|
||||
exclusiveSessions: [
|
||||
{
|
||||
sessionId: "lease:conn-active",
|
||||
ageMs: null,
|
||||
requestCount: 4,
|
||||
connectionId: "conn-active",
|
||||
connectionName: "Friendly active account",
|
||||
leaseBacked: true,
|
||||
active: true,
|
||||
},
|
||||
{
|
||||
sessionId: "lease:conn-idle",
|
||||
ageMs: null,
|
||||
requestCount: 0,
|
||||
connectionId: "conn-idle",
|
||||
connectionName: "Friendly idle account",
|
||||
leaseBacked: true,
|
||||
active: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(container.querySelector("[title='lease:conn-active']")).not.toBeNull();
|
||||
expect(container.querySelector("[title='lease:conn-idle']")).not.toBeNull();
|
||||
expect(container.querySelector("[title='legacy-unmanaged']")).not.toBeNull();
|
||||
expect(container.querySelector("[title='legacy-active']")).toBeNull();
|
||||
expect(container.textContent).toContain("Friendly active account");
|
||||
expect(container.textContent).toContain("Friendly idle account");
|
||||
|
||||
const activeLabels = Array.from(container.querySelectorAll("span")).filter(
|
||||
(node) => node.textContent === "Localized active"
|
||||
);
|
||||
expect(activeLabels).toHaveLength(1);
|
||||
const idleRow = container.querySelector("[title='lease:conn-idle']")?.closest("tr");
|
||||
expect(idleRow?.textContent).not.toContain("Localized active");
|
||||
expect(container.textContent).not.toContain("IDLE");
|
||||
|
||||
expect(container.querySelector("[data-testid='session-count']")?.textContent).toBe("3");
|
||||
});
|
||||
|
||||
it("preserves a legacy-only response when additive lease fields are absent", async () => {
|
||||
await renderPayload({
|
||||
count: 1,
|
||||
sessions: [
|
||||
{
|
||||
sessionId: "legacy-only",
|
||||
ageMs: 1_000,
|
||||
requestCount: 1,
|
||||
connectionId: null,
|
||||
},
|
||||
],
|
||||
byApiKey: {},
|
||||
});
|
||||
|
||||
expect(container.querySelector("[title='legacy-only']")).not.toBeNull();
|
||||
expect(container.textContent).not.toContain("Localized empty state");
|
||||
expect(container.querySelector("[data-testid='session-count']")?.textContent).toBe("1");
|
||||
});
|
||||
|
||||
it("keeps the localized empty state and zero count", async () => {
|
||||
await renderPayload({ count: 0, sessions: [], byApiKey: {} });
|
||||
|
||||
expect(container.textContent).toContain("Localized empty state");
|
||||
expect(container.querySelector("tbody")).toBeNull();
|
||||
expect(container.querySelector("[data-testid='session-count']")?.textContent).toBe("0");
|
||||
});
|
||||
Reference in New Issue
Block a user