test: clear codex command broad matchers

This commit is contained in:
Peter Steinberger
2026-05-10 15:39:57 +01:00
parent 22963259c9
commit 25efb80e30

View File

@@ -83,6 +83,28 @@ function escapeRegExp(value: string): string {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function requireResultText(result: PluginCommandResult): string {
if (typeof result.text !== "string") {
throw new Error("expected command result text");
}
return result.text;
}
function expectResultTextContains(result: PluginCommandResult, expected: string): void {
expect(requireResultText(result)).toContain(expected);
}
function requireRecord(value: unknown, message: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new Error(message);
}
return value as Record<string, unknown>;
}
function requireRequestParams(call: unknown[] | undefined): Record<string, unknown> {
return requireRecord(call?.[2], "expected request params object");
}
function expectedDiagnosticsTargetBlock(params: {
index?: number;
channel?: string;
@@ -190,17 +212,18 @@ describe("codex command", () => {
it("shows model ids from Codex app-server", async () => {
const config = { auth: { order: { "openai-codex": ["openai-codex:work"] } } };
const listCodexAppServerModels = vi.fn(async (_options?: { config?: unknown }) => ({
models: [
{
id: "gpt-5.4",
model: "gpt-5.4",
inputModalities: ["text"],
supportedReasoningEfforts: ["medium"],
},
],
}));
const deps = createDeps({
listCodexAppServerModels: vi.fn(async () => ({
models: [
{
id: "gpt-5.4",
model: "gpt-5.4",
inputModalities: ["text"],
supportedReasoningEfforts: ["medium"],
},
],
})),
listCodexAppServerModels,
});
await expect(
@@ -209,7 +232,8 @@ describe("codex command", () => {
text: "Codex models:\n- gpt-5.4",
});
expect(deps.requestOptions).toHaveBeenCalledWith(undefined, 100, config);
expect(deps.listCodexAppServerModels).toHaveBeenCalledWith(expect.objectContaining({ config }));
const modelsRequest = listCodexAppServerModels.mock.calls[0]?.[0];
expect(modelsRequest?.config).toBe(config);
});
it("shows when Codex app-server model output is truncated", async () => {
@@ -428,12 +452,10 @@ describe("codex command", () => {
.mockResolvedValueOnce(limits),
});
await expect(handleCodexCommand(createContext("status"), { deps })).resolves.toMatchObject({
text: expect.stringContaining("Rate limits: Codex: primary 42%"),
});
await expect(handleCodexCommand(createContext("account"), { deps })).resolves.toMatchObject({
text: expect.stringContaining("Rate limits: Codex: primary 42%"),
});
const statusResult = await handleCodexCommand(createContext("status"), { deps });
expectResultTextContains(statusResult, "Rate limits: Codex: primary 42%");
const accountResult = await handleCodexCommand(createContext("account"), { deps });
expectResultTextContains(accountResult, "Rate limits: Codex: primary 42%");
});
it("rejects extra operands for read-only Codex commands", async () => {
@@ -515,9 +537,13 @@ describe("codex command", () => {
expect(result.text).toContain("Account: codex@example.com");
expect(result.text).toContain("Rate limits: Codex: primary 50%, resets in");
expect(readRecentCodexRateLimits()).toMatchObject({
rateLimits: { limitId: "codex" },
});
const cachedLimits = requireRecord(
readRecentCodexRateLimits(),
"expected cached Codex rate limits",
);
expect(requireRecord(cachedLimits.rateLimits, "expected rate limits object").limitId).toBe(
"codex",
);
expect(safeCodexControlRequest).toHaveBeenCalledWith(undefined, CODEX_CONTROL_METHODS.account, {
refreshToken: false,
});
@@ -721,30 +747,26 @@ describe("codex command", () => {
"Computer Use is installed, but the computer-use plugin is disabled. Run /codex computer-use install or enable computerUse.autoInstall to re-enable it.",
}));
await expect(
handleCodexCommand(createContext("computer-use status"), {
deps: createDeps({ readCodexComputerUseStatus }),
}),
).resolves.toEqual({
text: expect.stringContaining("Plugin: computer-use (installed, disabled)"),
const result = await handleCodexCommand(createContext("computer-use status"), {
deps: createDeps({ readCodexComputerUseStatus }),
});
expectResultTextContains(result, "Plugin: computer-use (installed, disabled)");
});
it("installs Codex Computer Use from command overrides", async () => {
const installCodexComputerUse = vi.fn(async () => computerUseReadyStatus());
await expect(
handleCodexCommand(
createContext(
"computer-use install --source github:example/desktop-tools --marketplace desktop-tools",
),
{
deps: createDeps({ installCodexComputerUse }),
},
const result = await handleCodexCommand(
createContext(
"computer-use install --source github:example/desktop-tools --marketplace desktop-tools",
),
).resolves.toEqual({
text: expect.stringContaining("Computer Use: ready"),
});
{
deps: createDeps({ installCodexComputerUse }),
},
);
expectResultTextContains(result, "Computer Use: ready");
expect(installCodexComputerUse).toHaveBeenCalledWith({
pluginConfig: undefined,
forceEnable: true,
@@ -758,13 +780,11 @@ describe("codex command", () => {
it("shows help when Computer Use option values are missing", async () => {
const installCodexComputerUse = vi.fn(async () => computerUseReadyStatus());
await expect(
handleCodexCommand(createContext("computer-use install --source"), {
deps: createDeps({ installCodexComputerUse }),
}),
).resolves.toEqual({
text: expect.stringContaining("Usage: /codex computer-use"),
const result = await handleCodexCommand(createContext("computer-use install --source"), {
deps: createDeps({ installCodexComputerUse }),
});
expectResultTextContains(result, "Usage: /codex computer-use");
expect(installCodexComputerUse).not.toHaveBeenCalled();
});
@@ -772,13 +792,11 @@ describe("codex command", () => {
const readCodexComputerUseStatus = vi.fn(async () => computerUseReadyStatus());
const installCodexComputerUse = vi.fn(async () => computerUseReadyStatus());
await expect(
handleCodexCommand(createContext("computer-use status install"), {
deps: createDeps({ readCodexComputerUseStatus, installCodexComputerUse }),
}),
).resolves.toEqual({
text: expect.stringContaining("Usage: /codex computer-use"),
const result = await handleCodexCommand(createContext("computer-use status install"), {
deps: createDeps({ readCodexComputerUseStatus, installCodexComputerUse }),
});
expectResultTextContains(result, "Usage: /codex computer-use");
expect(readCodexComputerUseStatus).not.toHaveBeenCalled();
expect(installCodexComputerUse).not.toHaveBeenCalled();
});
@@ -833,7 +851,7 @@ describe("codex command", () => {
"This request expires in 5 minutes.",
].join("\n"),
);
expect(request.interactive).toMatchObject({
expect(request.interactive).toEqual({
blocks: [
{
type: "buttons",
@@ -843,7 +861,11 @@ describe("codex command", () => {
value: `/codex diagnostics confirm ${token}`,
style: "danger",
},
{ label: "Cancel", value: `/codex diagnostics cancel ${token}` },
{
label: "Cancel",
value: `/codex diagnostics cancel ${token}`,
style: "secondary",
},
],
},
],
@@ -926,11 +948,11 @@ describe("codex command", () => {
});
expect(safeCodexControlRequest).not.toHaveBeenCalled();
await expect(
handleCodexCommand(createContext(`diagnostics confirm ${token}`, sessionFile), { deps }),
).resolves.toMatchObject({
text: expect.stringContaining("Codex diagnostics sent to OpenAI servers:"),
});
const confirmResult = await handleCodexCommand(
createContext(`diagnostics confirm ${token}`, sessionFile),
{ deps },
);
expectResultTextContains(confirmResult, "Codex diagnostics sent to OpenAI servers:");
expect(safeCodexControlRequest).toHaveBeenCalledTimes(1);
});
@@ -1112,18 +1134,16 @@ describe("codex command", () => {
].join("\n"),
});
expect(safeCodexControlRequest).toHaveBeenCalledTimes(2);
expect(safeCodexControlRequest).toHaveBeenNthCalledWith(
1,
undefined,
CODEX_CONTROL_METHODS.feedback,
expect.objectContaining({ threadId: "thread-111", includeLogs: true }),
);
expect(safeCodexControlRequest).toHaveBeenNthCalledWith(
2,
undefined,
CODEX_CONTROL_METHODS.feedback,
expect.objectContaining({ threadId: "thread-222", includeLogs: true }),
);
expect(safeCodexControlRequest.mock.calls[0]?.[0]).toBeUndefined();
expect(safeCodexControlRequest.mock.calls[0]?.[1]).toBe(CODEX_CONTROL_METHODS.feedback);
const firstFeedbackParams = requireRequestParams(safeCodexControlRequest.mock.calls[0]);
expect(firstFeedbackParams.threadId).toBe("thread-111");
expect(firstFeedbackParams.includeLogs).toBe(true);
expect(safeCodexControlRequest.mock.calls[1]?.[0]).toBeUndefined();
expect(safeCodexControlRequest.mock.calls[1]?.[1]).toBe(CODEX_CONTROL_METHODS.feedback);
const secondFeedbackParams = requireRequestParams(safeCodexControlRequest.mock.calls[1]);
expect(secondFeedbackParams.threadId).toBe("thread-222");
expect(secondFeedbackParams.includeLogs).toBe(true);
});
it("requires an owner for Codex diagnostics feedback uploads", async () => {
@@ -1251,9 +1271,8 @@ describe("codex command", () => {
});
releaseFirstConfirmBindingRead();
await expect(firstConfirm).resolves.toMatchObject({
text: expect.stringContaining("Codex diagnostics sent to OpenAI servers:"),
});
const firstConfirmResult = await firstConfirm;
expectResultTextContains(firstConfirmResult, "Codex diagnostics sent to OpenAI servers:");
expect(safeCodexControlRequest).toHaveBeenCalledTimes(1);
});
@@ -1304,10 +1323,12 @@ describe("codex command", () => {
`${sessionFile}.codex-app-server.json`,
JSON.stringify({ schemaVersion: 1, threadId: "thread-private", cwd: "/repo" }),
);
const safeCodexControlRequest = vi.fn(async () => ({
ok: true as const,
value: { threadId: "thread-private" },
}));
const safeCodexControlRequest = vi.fn(
async (_pluginConfig: unknown, _method: string, _requestParams: unknown) => ({
ok: true as const,
value: { threadId: "thread-private" },
}),
);
const deps = createDeps({ safeCodexControlRequest });
const request = await handleCodexCommand(
@@ -1342,15 +1363,12 @@ describe("codex command", () => {
"Included Codex logs and spawned Codex subthreads when available.",
].join("\n"),
});
expect(safeCodexControlRequest).toHaveBeenCalledWith(
undefined,
CODEX_CONTROL_METHODS.feedback,
expect.objectContaining({
classification: "bug",
threadId: "thread-private",
includeLogs: true,
}),
);
expect(safeCodexControlRequest.mock.calls[0]?.[0]).toBeUndefined();
expect(safeCodexControlRequest.mock.calls[0]?.[1]).toBe(CODEX_CONTROL_METHODS.feedback);
const feedbackParams = requireRequestParams(safeCodexControlRequest.mock.calls[0]);
expect(feedbackParams.classification).toBe("bug");
expect(feedbackParams.threadId).toBe("thread-private");
expect(feedbackParams.includeLogs).toBe(true);
});
it("keeps diagnostics confirmation eviction scoped to account identity", async () => {
@@ -1405,10 +1423,12 @@ describe("codex command", () => {
`${sessionFile}.codex-app-server.json`,
JSON.stringify({ schemaVersion: 1, threadId: "thread-789", cwd: "/repo" }),
);
const safeCodexControlRequest = vi.fn(async () => ({
ok: true as const,
value: { threadId: "thread-789" },
}));
const safeCodexControlRequest = vi.fn(
async (_pluginConfig: unknown, _method: string, _requestParams: unknown) => ({
ok: true as const,
value: { threadId: "thread-789" },
}),
);
const note = "x".repeat(2050);
const deps = createDeps({ safeCodexControlRequest });
@@ -1418,13 +1438,10 @@ describe("codex command", () => {
const token = readDiagnosticsConfirmationToken(request);
await handleCodexCommand(createContext(`diagnostics confirm ${token}`, sessionFile), { deps });
expect(safeCodexControlRequest).toHaveBeenCalledWith(
undefined,
CODEX_CONTROL_METHODS.feedback,
expect.objectContaining({
reason: "x".repeat(2048),
}),
);
expect(safeCodexControlRequest.mock.calls[0]?.[0]).toBeUndefined();
expect(safeCodexControlRequest.mock.calls[0]?.[1]).toBe(CODEX_CONTROL_METHODS.feedback);
const feedbackParams = requireRequestParams(safeCodexControlRequest.mock.calls[0]);
expect(feedbackParams.reason).toBe("x".repeat(2048));
});
it("escapes diagnostics notes before showing approval text", async () => {
@@ -1544,17 +1561,14 @@ describe("codex command", () => {
{ deps },
);
const firstToken = readDiagnosticsConfirmationToken(firstRequest);
await expect(
handleCodexCommand(
createContext(`diagnostics confirm ${firstToken}`, sessionFile, {
accountId: "account-1",
channelId: "channel-1",
}),
{ deps },
),
).resolves.toMatchObject({
text: expect.stringContaining("Codex diagnostics sent to OpenAI servers:"),
});
const firstConfirmResult = await handleCodexCommand(
createContext(`diagnostics confirm ${firstToken}`, sessionFile, {
accountId: "account-1",
channelId: "channel-1",
}),
{ deps },
);
expectResultTextContains(firstConfirmResult, "Codex diagnostics sent to OpenAI servers:");
await fs.writeFile(
`${sessionFile}.codex-app-server.json`,
@@ -1568,17 +1582,14 @@ describe("codex command", () => {
{ deps },
);
const secondToken = readDiagnosticsConfirmationToken(secondRequest);
await expect(
handleCodexCommand(
createContext(`diagnostics confirm ${secondToken}`, sessionFile, {
accountId: "account-2",
channelId: "channel-2",
}),
{ deps },
),
).resolves.toMatchObject({
text: expect.stringContaining("Codex diagnostics sent to OpenAI servers:"),
});
const secondConfirmResult = await handleCodexCommand(
createContext(`diagnostics confirm ${secondToken}`, sessionFile, {
accountId: "account-2",
channelId: "channel-2",
}),
{ deps },
);
expectResultTextContains(secondConfirmResult, "Codex diagnostics sent to OpenAI servers:");
expect(safeCodexControlRequest).toHaveBeenCalledTimes(2);
});
@@ -1605,14 +1616,11 @@ describe("codex command", () => {
{ deps },
);
const firstToken = readDiagnosticsConfirmationToken(firstRequest);
await expect(
handleCodexCommand(
createContext(`diagnostics confirm ${firstToken}`, sessionFile, firstScope),
{ deps },
),
).resolves.toMatchObject({
text: expect.stringContaining("Codex diagnostics sent to OpenAI servers:"),
});
const firstConfirmResult = await handleCodexCommand(
createContext(`diagnostics confirm ${firstToken}`, sessionFile, firstScope),
{ deps },
);
expectResultTextContains(firstConfirmResult, "Codex diagnostics sent to OpenAI servers:");
await fs.writeFile(
`${sessionFile}.codex-app-server.json`,
@@ -1627,14 +1635,11 @@ describe("codex command", () => {
{ deps },
);
const secondToken = readDiagnosticsConfirmationToken(secondRequest);
await expect(
handleCodexCommand(
createContext(`diagnostics confirm ${secondToken}`, sessionFile, secondScope),
{ deps },
),
).resolves.toMatchObject({
text: expect.stringContaining("Codex diagnostics sent to OpenAI servers:"),
});
const secondConfirmResult = await handleCodexCommand(
createContext(`diagnostics confirm ${secondToken}`, sessionFile, secondScope),
{ deps },
);
expectResultTextContains(secondConfirmResult, "Codex diagnostics sent to OpenAI servers:");
expect(safeCodexControlRequest).toHaveBeenCalledTimes(2);
});
@@ -1661,14 +1666,11 @@ describe("codex command", () => {
{ deps },
);
const firstToken = readDiagnosticsConfirmationToken(firstRequest);
await expect(
handleCodexCommand(
createContext(`diagnostics confirm ${firstToken}`, sessionFile, firstScope),
{ deps },
),
).resolves.toMatchObject({
text: expect.stringContaining("Codex diagnostics sent to OpenAI servers:"),
});
const firstConfirmResult = await handleCodexCommand(
createContext(`diagnostics confirm ${firstToken}`, sessionFile, firstScope),
{ deps },
);
expectResultTextContains(firstConfirmResult, "Codex diagnostics sent to OpenAI servers:");
await fs.writeFile(
`${sessionFile}.codex-app-server.json`,
@@ -1683,14 +1685,11 @@ describe("codex command", () => {
{ deps },
);
const secondToken = readDiagnosticsConfirmationToken(secondRequest);
await expect(
handleCodexCommand(
createContext(`diagnostics confirm ${secondToken}`, sessionFile, secondScope),
{ deps },
),
).resolves.toMatchObject({
text: expect.stringContaining("Codex diagnostics sent to OpenAI servers:"),
});
const secondConfirmResult = await handleCodexCommand(
createContext(`diagnostics confirm ${secondToken}`, sessionFile, secondScope),
{ deps },
);
expectResultTextContains(secondConfirmResult, "Codex diagnostics sent to OpenAI servers:");
expect(safeCodexControlRequest).toHaveBeenCalledTimes(2);
});
@@ -1937,7 +1936,7 @@ describe("codex command", () => {
sessionFile,
workspaceDir: "/repo",
}));
const requestConversationBinding = vi.fn(async () => ({
const requestConversationBinding = vi.fn(async (_request?: { summary?: string }) => ({
status: "bound" as const,
binding: {
bindingId: "binding-1",
@@ -1999,7 +1998,7 @@ describe("codex command", () => {
sessionFile,
workspaceDir: "/repo with space",
}));
const requestConversationBinding = vi.fn(async () => ({
const requestConversationBinding = vi.fn(async (_request?: { summary?: string }) => ({
status: "bound" as const,
binding: {
bindingId: "binding-1",
@@ -2048,7 +2047,7 @@ describe("codex command", () => {
sessionFile,
workspaceDir: unsafeWorkspace,
}));
const requestConversationBinding = vi.fn(async () => ({
const requestConversationBinding = vi.fn(async (_request?: { summary?: string }) => ({
status: "bound" as const,
binding: {
bindingId: "binding-1",
@@ -2077,11 +2076,9 @@ describe("codex command", () => {
expect(result.text).toContain("/repo \uff3btrusted\uff3d\uff08https://evil\uff09");
expect(result.text).not.toContain("<@U123>");
expect(result.text).not.toContain("[trusted](https://evil)");
expect(requestConversationBinding).toHaveBeenCalledWith(
expect.objectContaining({
summary:
"Codex app-server thread thread-123 &lt;\uff20U123&gt; in /repo \uff3btrusted\uff3d\uff08https://evil\uff09",
}),
const bindingRequest = requestConversationBinding.mock.calls[0]?.[0];
expect(bindingRequest?.summary).toBe(
"Codex app-server thread thread-123 &lt;\uff20U123&gt; in /repo \uff3btrusted\uff3d\uff08https://evil\uff09",
);
});