test: harden OpenAI response error handling
This commit is contained in:
+4
-7
@@ -7,11 +7,11 @@ ChatGPT MCP Server
|
|||||||
## Status
|
## Status
|
||||||
|
|
||||||
Planning complete.
|
Planning complete.
|
||||||
Phase 0 complete. Phase 1 complete. Phase 2 in progress.
|
Phase 0 complete. Phase 1 complete. Phase 2 complete.
|
||||||
|
|
||||||
## Current Phase
|
## Current Phase
|
||||||
|
|
||||||
Phase 2 - OpenAI Integration
|
Phase 3 — Not yet defined
|
||||||
|
|
||||||
## Completed Tasks
|
## Completed Tasks
|
||||||
|
|
||||||
@@ -23,11 +23,8 @@ Phase 2 - OpenAI Integration
|
|||||||
- Task 1.4 — Safe logging helper (`src/utils/logging.js`) ✅
|
- Task 1.4 — Safe logging helper (`src/utils/logging.js`) ✅
|
||||||
- Task 2.1 — OpenAI client wrapper (`src/openai/client.js`) ✅
|
- Task 2.1 — OpenAI client wrapper (`src/openai/client.js`) ✅
|
||||||
- Task 2.2 — Response builder (`src/openai/responses.js`) ✅
|
- Task 2.2 — Response builder (`src/openai/responses.js`) ✅
|
||||||
|
- Task 2.3 — Error handling and edge cases for OpenAI integration (tests) ✅
|
||||||
|
|
||||||
## Next Phase
|
## Next Phase
|
||||||
|
|
||||||
Phase 2 - OpenAI Integration
|
Phase 3 — Not yet defined
|
||||||
|
|
||||||
## Next Pending Task
|
|
||||||
|
|
||||||
Task 2.3 — Error handling and edge cases for OpenAI integration
|
|
||||||
|
|||||||
@@ -127,4 +127,8 @@ Requirements:
|
|||||||
- Mock OpenAI SDK network timeout errors separately from rate-limit errors.
|
- 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.
|
- 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;
|
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 ---
|
// --- Import ---
|
||||||
|
|
||||||
const { sendOpenAIResponse } = await import("../../src/openai/responses.js");
|
const { sendOpenAIResponse } = await import("../../src/openai/responses.js");
|
||||||
@@ -167,4 +179,38 @@ describe("sendOpenAIResponse", () => {
|
|||||||
sendOpenAIResponse(mockClient, { input: ["x"], model: "m", maxOutputTokens: -5 })
|
sendOpenAIResponse(mockClient, { input: ["x"], model: "m", maxOutputTokens: -5 })
|
||||||
).rejects.toHaveProperty("kind", "ValidationError");
|
).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