Files
NetMesh/electron/bridges/ai/codexHelpers.test.cjs
zhaolei 3c72efcb7f
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
[Init] Initial commit - NetMesh terminal manager
2026-09-13 18:24:01 +08:00

180 lines
5.8 KiB
JavaScript
Raw Blame History

const test = require("node:test");
const assert = require("node:assert/strict");
const {
MAX_CODEX_LOGIN_OUTPUT_CHARS,
MAX_CODEX_LOGIN_OUTPUT_BYTES,
MAX_CODEX_LOGIN_TERMINAL_SESSIONS,
CODEX_LOGIN_KILL_GRACE_MS,
appendCodexLoginOutput,
createCodexLoginOutputDecoder,
appendCodexChatGptValidationFailure,
codexLoginSessions,
extractCodexError,
isCodexAuthError,
normalizeCodexIntegrationState,
recordCodexLoginSession,
stopCodexLoginProcess,
} = require("./codexHelpers.cjs");
test("Codex login output keeps a bounded tail", () => {
const session = { output: "", url: null };
appendCodexLoginOutput(session, "a".repeat(MAX_CODEX_LOGIN_OUTPUT_CHARS));
appendCodexLoginOutput(session, "tail-marker");
assert.equal(session.output.length, MAX_CODEX_LOGIN_OUTPUT_CHARS);
assert.match(session.output, /tail-marker$/);
});
test("Codex login output limit is measured in UTF-8 bytes without cutting characters", () => {
const session = { output: "", url: null };
appendCodexLoginOutput(session, "中".repeat(MAX_CODEX_LOGIN_OUTPUT_BYTES));
assert.ok(Buffer.byteLength(session.output, "utf8") <= MAX_CODEX_LOGIN_OUTPUT_BYTES);
assert.doesNotMatch(session.output, /<2F>/u);
assert.match(session.output, /^中+$/u);
});
test("Codex login stdout and stderr decode split UTF-8 independently when interleaved", () => {
const session = { output: "", url: null };
const stdout = createCodexLoginOutputDecoder(session);
const stderr = createCodexLoginOutputDecoder(session);
const outBytes = Buffer.from("中文", "utf8");
const errBytes = Buffer.from("错误", "utf8");
stdout.write(outBytes.subarray(0, 2));
stderr.write(errBytes.subarray(0, 1));
stdout.write(outBytes.subarray(2));
stderr.write(errBytes.subarray(1));
stdout.end();
stderr.end();
assert.equal(session.output, "中文错误");
});
test("Codex login history retains only bounded terminal sessions", (t) => {
t.after(() => codexLoginSessions.clear());
codexLoginSessions.clear();
recordCodexLoginSession({ id: "running", state: "running", process: { killed: false } });
for (let index = 0; index < MAX_CODEX_LOGIN_TERMINAL_SESSIONS + 3; index += 1) {
recordCodexLoginSession({ id: `done-${index}`, state: "success", process: null });
}
assert.equal(codexLoginSessions.has("running"), true);
assert.equal(codexLoginSessions.has("done-0"), false);
assert.equal(codexLoginSessions.size, MAX_CODEX_LOGIN_TERMINAL_SESSIONS + 1);
});
test("a newly completed Codex login remains available while older records are pruned", (t) => {
t.after(() => codexLoginSessions.clear());
codexLoginSessions.clear();
for (let index = 0; index < MAX_CODEX_LOGIN_TERMINAL_SESSIONS; index += 1) {
recordCodexLoginSession({ id: `old-${index}`, state: "success", process: null });
}
const current = { id: "current", state: "running", process: { killed: false } };
recordCodexLoginSession(current);
current.state = "success";
current.process = null;
recordCodexLoginSession(current);
assert.equal(codexLoginSessions.size, MAX_CODEX_LOGIN_TERMINAL_SESSIONS);
assert.equal(codexLoginSessions.has("current"), true);
assert.equal(codexLoginSessions.has("old-0"), false);
});
test("live cancelled login processes stay tracked until they close", (t) => {
t.after(() => codexLoginSessions.clear());
codexLoginSessions.clear();
for (let index = 0; index < MAX_CODEX_LOGIN_TERMINAL_SESSIONS + 2; index += 1) {
recordCodexLoginSession({
id: `cancelled-${index}`,
state: "cancelled",
process: { kill() {} },
});
}
assert.equal(codexLoginSessions.size, MAX_CODEX_LOGIN_TERMINAL_SESSIONS + 2);
});
test("Codex login cancellation escalates from TERM to KILL", () => {
const signals = [];
const scheduled = [];
let escalate;
const session = {
process: { kill: (signal) => signals.push(signal) },
killTimer: null,
};
stopCodexLoginProcess(session, {
setTimeoutFn: (callback, delay) => {
scheduled.push(delay);
escalate = callback;
return { unref() {} };
},
});
escalate();
assert.deepEqual(signals, ["SIGTERM", "SIGKILL"]);
assert.deepEqual(scheduled, [CODEX_LOGIN_KILL_GRACE_MS]);
});
test("normalizeCodexIntegrationState recognizes ChatGPT login status", () => {
assert.equal(
normalizeCodexIntegrationState("Logged in using ChatGPT"),
"connected_chatgpt",
);
});
test("appendCodexChatGptValidationFailure preserves the login status output", () => {
const output = appendCodexChatGptValidationFailure(
"Logged in using ChatGPT",
"SDK probe failed",
);
assert.match(output, /Logged in using ChatGPT/);
assert.match(output, /ChatGPT auth validation failed:/);
assert.match(output, /SDK probe failed/);
assert.equal(normalizeCodexIntegrationState(output), "connected_chatgpt");
});
test("isCodexAuthError recognizes auth failures stored in error text", () => {
assert.equal(
isCodexAuthError({ ok: false, error: "401 Unauthorized: authentication required" }),
true,
);
});
test("extractCodexError preserves nested error object messages", () => {
const normalized = extractCodexError({
error: {
code: "model_not_found",
message: "Model gpt-test is not available",
},
});
assert.deepEqual(normalized, {
message: "Model gpt-test is not available",
code: "model_not_found",
});
});
test("extractCodexError stringifies unknown object errors instead of [object Object]", () => {
const normalized = extractCodexError({
status: 400,
detail: "Bad request",
});
assert.equal(normalized.message, '{"status":400,"detail":"Bad request"}');
assert.equal(normalized.code, undefined);
});
test("extractCodexError handles circular structured errors", () => {
const error = { status: 500 };
error.self = error;
const normalized = extractCodexError(error);
assert.equal(normalized.message, '{"status":500,"self":"[Circular]"}');
});