Files
diegosouzapw--omniroute/tests/unit/request-log-detail-layout.test.ts
2026-07-13 13:39:12 +08:00

229 lines
7.4 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import test from "node:test";
import assert from "node:assert/strict";
import React from "react";
import { renderToStaticMarkup } from "react-dom/server";
const { default: RequestLoggerDetail } =
await import("../../src/shared/components/RequestLoggerDetail.tsx");
function renderDetailWithSourceFormat(sourceFormat: string) {
return renderToStaticMarkup(
React.createElement(RequestLoggerDetail, {
log: {
status: 200,
method: "POST",
path: "/v1/chat/completions",
timestamp: "2026-04-09T21:27:08.000Z",
duration: 2500,
provider: "openrouter",
sourceFormat,
model: "deepseek/deepseek-v4-pro",
requestedModel: "openrouter/deepseek/deepseek-v4-pro",
cacheSource: "upstream",
tokens: {
in: 10,
out: 2,
cacheRead: null,
cacheWrite: null,
reasoning: null,
},
},
detail: {
requestedModel: "openrouter/deepseek/deepseek-v4-pro",
cacheSource: "upstream",
tokens: {
in: 10,
out: 2,
cacheRead: null,
cacheWrite: null,
reasoning: null,
},
},
loading: false,
onClose: () => {},
onCopy: async () => true,
})
);
}
test("request log detail splits token badges into input and output groups", () => {
const html = renderToStaticMarkup(
React.createElement(RequestLoggerDetail, {
log: {
status: 200,
method: "POST",
path: "/v1/chat/completions",
timestamp: "2026-04-09T21:27:08.000Z",
duration: 2500,
provider: "openai-compatible-sp-openai",
sourceFormat: "openai-chat",
model: "gpt-5.4",
requestedModel: "openai-compatible-sp-openai/gpt-5.4",
account: "main",
apiKeyName: "tools",
apiKeyId: "29d9***7e37",
comboName: "_Latest-Discounted",
cacheSource: "semantic",
tokens: {
in: 21818,
out: 42,
cacheRead: 21632,
cacheWrite: null,
reasoning: null,
},
},
detail: {
account: "main",
apiKeyName: "tools",
apiKeyId: "29d9***7e37",
comboName: "_Latest-Discounted",
requestedModel: "openai-compatible-sp-openai/gpt-5.4",
cacheSource: "semantic",
tokens: {
in: 21818,
out: 42,
cacheRead: 21632,
cacheWrite: null,
reasoning: null,
},
},
loading: false,
onClose: () => {},
onCopy: async () => true,
})
);
const inputLabelIndex = html.indexOf(">Input<");
const outputLabelIndex = html.indexOf(">Output<");
const modelLabelIndex = html.indexOf(">Model<");
const requestedModelLabelIndex = html.indexOf(">Requested Model<");
// The completed-request detail grid renders "Started At" / "Ended At" columns
// (renamed from the old single "Completed Time" label in #5834).
assert.notEqual(html.indexOf(">Ended At<"), -1);
assert.equal(html.includes(">Time<"), false);
assert.notEqual(inputLabelIndex, -1);
assert.notEqual(outputLabelIndex, -1);
assert.notEqual(modelLabelIndex, -1);
assert.notEqual(requestedModelLabelIndex, -1);
assert.equal(html.includes(">Tokens<"), false);
assert.equal(inputLabelIndex < outputLabelIndex, true);
assert.equal(outputLabelIndex < modelLabelIndex, true);
assert.equal(modelLabelIndex < requestedModelLabelIndex, true);
assert.notEqual(html.indexOf(">Cache Source<"), -1);
assert.notEqual(html.indexOf(">Semantic (OmniRoute)<"), -1);
assert.notEqual(html.indexOf(">OpenAI-Chat<"), -1);
assert.match(
html,
/data-testid="token-group-input"[\s\S]*Total In: 21[\s\S]*818[\s\S]*Cache Read: 21[\s\S]*632[\s\S]*Cache Write: N\/A/
);
assert.match(html, /data-testid="token-group-output"[\s\S]*Total Out: 42[\s\S]*Reasoning: N\/A/);
});
test("request log detail labels OpenAI protocol variants explicitly", () => {
const chatHtml = renderDetailWithSourceFormat("openai");
const responsesHtml = renderDetailWithSourceFormat("openai-responses");
assert.notEqual(chatHtml.indexOf(">OpenAI-Chat<"), -1);
assert.notEqual(responsesHtml.indexOf(">OpenAI-Responses<"), -1);
});
test("request log detail compression-summary badge shows positive saved%, never negative", () => {
// Regression: prior code used `(-{pct}%)` which produced literal "-100%" when the entire
// prompt was compressed (compressed=5286, totalIn=0). The fix clamps pct to [0, 100] and
// uses "(N% saved)" so the user-facing label is always positive.
const make = (tokensIn: number, tokensCompressed: number) =>
renderToStaticMarkup(
React.createElement(RequestLoggerDetail, {
log: {
status: 200,
method: "POST",
path: "/v1/chat/completions",
timestamp: "2026-04-09T21:27:08.000Z",
duration: 1500,
provider: "openai-compatible-sp-openai",
sourceFormat: "openai-chat",
model: "gpt-5.4",
requestedModel: "openai-compatible-sp-openai/gpt-5.4",
cacheSource: "semantic",
tokens: {
in: tokensIn,
out: 42,
cacheRead: null,
cacheWrite: null,
reasoning: null,
compressed: tokensCompressed,
},
},
detail: {
tokens: {
in: tokensIn,
out: 42,
cacheRead: null,
cacheWrite: null,
reasoning: null,
compressed: tokensCompressed,
},
},
loading: false,
onClose: () => {},
onCopy: async () => true,
})
);
// Original bug repro: totalIn=0, compressed=5286 → previously rendered "(100%)".
const fullyCompressed = make(0, 5286);
assert.match(fullyCompressed, /Compressed: 5,286 → 0 \(100% saved\)/);
assert.equal(fullyCompressed.includes("(-100%)"), false, "literal '(-100%)' must never appear");
assert.equal(
fullyCompressed.includes("\u2212100%"),
false,
"unicode minus + 100% must never appear"
);
// Half-compressed case must clamp cleanly inside the [0, 100] window.
const halfCompressed = make(2500, 2500);
assert.match(halfCompressed, /Compressed: 5,000 → 2,500 \(50% saved\)/);
assert.equal(halfCompressed.includes("-50%"), false);
// Sanity: tiny input, tiny savings still rendered as a positive percentage.
const small = make(1000, 100);
assert.match(small, /Compressed: 1,100 → 1,000 \(9% saved\)/);
});
test("request log detail follows the email visibility setting for accounts", () => {
const props = {
log: {
status: 200,
method: "POST",
path: "/v1/responses",
timestamp: "2026-04-09T21:27:08.000Z",
duration: 2500,
provider: "codex",
sourceFormat: "openai-responses",
model: "gpt-5.5",
account: "logs.user@example.com",
tokens: { in: 10, out: 2 },
},
detail: {
account: "logs.user@example.com",
tokens: { in: 10, out: 2 },
},
loading: false,
onClose: () => {},
onCopy: async () => true,
};
const hiddenHtml = renderToStaticMarkup(
React.createElement(RequestLoggerDetail, { ...props, emailsVisible: false })
);
const visibleHtml = renderToStaticMarkup(
React.createElement(RequestLoggerDetail, { ...props, emailsVisible: true })
);
assert.match(hiddenHtml, /log\*{6}@\*{8}com/);
assert.equal(hiddenHtml.includes("logs.user@example.com"), false);
assert.notEqual(visibleHtml.indexOf("logs.user@example.com"), -1);
});