test: harden OpenAI response error handling
This commit is contained in:
+4
-7
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user