test: harden OpenAI response error handling

This commit is contained in:
2026-06-11 11:47:29 +01:00
parent 189b3372a4
commit 311f49435e
3 changed files with 55 additions and 8 deletions
+4 -7
View File
@@ -7,11 +7,11 @@ ChatGPT MCP Server
## Status
Planning complete.
Phase 0 complete. Phase 1 complete. Phase 2 in progress.
Phase 0 complete. Phase 1 complete. Phase 2 complete.
## Current Phase
Phase 2 - OpenAI Integration
Phase 3 — Not yet defined
## Completed Tasks
@@ -23,11 +23,8 @@ Phase 2 - OpenAI Integration
- Task 1.4 — Safe logging helper (`src/utils/logging.js`) ✅
- Task 2.1 — OpenAI client wrapper (`src/openai/client.js`) ✅
- Task 2.2 — Response builder (`src/openai/responses.js`) ✅
- Task 2.3 — Error handling and edge cases for OpenAI integration (tests) ✅
## Next Phase
Phase 2 - OpenAI Integration
## Next Pending Task
Task 2.3 — Error handling and edge cases for OpenAI integration
Phase 3 — Not yet defined
+5 -1
View File
@@ -127,4 +127,8 @@ Requirements:
- Mock OpenAI SDK network timeout errors separately from rate-limit errors.
- Verify that no API keys or secrets are ever leaked in error messages across all tested paths.
Status: ⏳ Pending
Status: ✅ Complete
---
Phase 2 complete. Phase 3 not yet defined.
+46
View File
@@ -30,6 +30,18 @@ function makeNetworkError() {
return err;
}
function makeServerError() {
const err = new Error("Service unavailable");
err.status = 503;
return err;
}
function makeTimeoutError() {
const err = new Error("timeout");
err.code = "ETIMEDOUT";
return err;
}
// --- Import ---
const { sendOpenAIResponse } = await import("../../src/openai/responses.js");
@@ -167,4 +179,38 @@ describe("sendOpenAIResponse", () => {
sendOpenAIResponse(mockClient, { input: ["x"], model: "m", maxOutputTokens: -5 })
).rejects.toHaveProperty("kind", "ValidationError");
});
it("throws ValidationError when maxOutputTokens is zero", async () => {
await expect(
sendOpenAIResponse(mockClient, { input: ["x"], model: "m", maxOutputTokens: 0 })
).rejects.toHaveProperty("kind", "ValidationError");
});
it("throws ValidationError when maxOutputTokens is a non-positive float", async () => {
await expect(
sendOpenAIResponse(mockClient, { input: ["x"], model: "m", maxOutputTokens: 0.5 })
).rejects.toHaveProperty("kind", "ValidationError");
});
it("throws ValidationError when maxOutputTokens is NaN", async () => {
await expect(
sendOpenAIResponse(mockClient, { input: ["x"], model: "m", maxOutputTokens: NaN })
).rejects.toHaveProperty("kind", "ValidationError");
});
// ===== Error mapping edge cases (2) =====
it("maps status 503 to OpenAIRequestError", async () => {
mockCreate.mockRejectedValueOnce(makeServerError());
await expect(
sendOpenAIResponse(mockClient, { input: ["x"], model: "m" })
).rejects.toHaveProperty("kind", "OpenAIRequestError");
});
it("maps timeout error (code ETIMEDOUT) to OpenAIRequestError", async () => {
mockCreate.mockRejectedValueOnce(makeTimeoutError());
await expect(
sendOpenAIResponse(mockClient, { input: ["x"], model: "m" })
).rejects.toHaveProperty("kind", "OpenAIRequestError");
});
});