|
|
@@ -9,10 +9,21 @@ import OpenAI from "openai"
|
|
|
import { OpenRouterHandler } from "../openrouter"
|
|
|
import { ApiHandlerOptions } from "../../../shared/api"
|
|
|
import { Package } from "../../../shared/package"
|
|
|
+import { ApiProviderError } from "@roo-code/types"
|
|
|
|
|
|
// Mock dependencies
|
|
|
vitest.mock("openai")
|
|
|
vitest.mock("delay", () => ({ default: vitest.fn(() => Promise.resolve()) }))
|
|
|
+
|
|
|
+// Mock TelemetryService
|
|
|
+const mockCaptureException = vitest.fn()
|
|
|
+vitest.mock("@roo-code/telemetry", () => ({
|
|
|
+ TelemetryService: {
|
|
|
+ instance: {
|
|
|
+ captureException: (...args: unknown[]) => mockCaptureException(...args),
|
|
|
+ },
|
|
|
+ },
|
|
|
+}))
|
|
|
vitest.mock("../fetchers/modelCache", () => ({
|
|
|
getModels: vitest.fn().mockImplementation(() => {
|
|
|
return Promise.resolve({
|
|
|
@@ -267,7 +278,7 @@ describe("OpenRouterHandler", () => {
|
|
|
)
|
|
|
})
|
|
|
|
|
|
- it("handles API errors", async () => {
|
|
|
+ it("handles API errors and captures telemetry", async () => {
|
|
|
const handler = new OpenRouterHandler(mockOptions)
|
|
|
const mockStream = {
|
|
|
async *[Symbol.asyncIterator]() {
|
|
|
@@ -282,6 +293,52 @@ describe("OpenRouterHandler", () => {
|
|
|
|
|
|
const generator = handler.createMessage("test", [])
|
|
|
await expect(generator.next()).rejects.toThrow("OpenRouter API Error 500: API Error")
|
|
|
+
|
|
|
+ // Verify telemetry was captured
|
|
|
+ expect(mockCaptureException).toHaveBeenCalledWith(expect.any(ApiProviderError), {
|
|
|
+ provider: "OpenRouter",
|
|
|
+ modelId: mockOptions.openRouterModelId,
|
|
|
+ operation: "createMessage",
|
|
|
+ errorCode: 500,
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ it("captures telemetry when createMessage throws an exception", async () => {
|
|
|
+ const handler = new OpenRouterHandler(mockOptions)
|
|
|
+ const mockCreate = vitest.fn().mockRejectedValue(new Error("Connection failed"))
|
|
|
+ ;(OpenAI as any).prototype.chat = {
|
|
|
+ completions: { create: mockCreate },
|
|
|
+ } as any
|
|
|
+
|
|
|
+ const generator = handler.createMessage("test", [])
|
|
|
+ await expect(generator.next()).rejects.toThrow()
|
|
|
+
|
|
|
+ // Verify telemetry was captured
|
|
|
+ expect(mockCaptureException).toHaveBeenCalledWith(expect.any(ApiProviderError), {
|
|
|
+ provider: "OpenRouter",
|
|
|
+ modelId: mockOptions.openRouterModelId,
|
|
|
+ operation: "createMessage",
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ it("does NOT capture telemetry for 429 rate limit errors", async () => {
|
|
|
+ const handler = new OpenRouterHandler(mockOptions)
|
|
|
+ const mockStream = {
|
|
|
+ async *[Symbol.asyncIterator]() {
|
|
|
+ yield { error: { message: "Rate limit exceeded", code: 429 } }
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ const mockCreate = vitest.fn().mockResolvedValue(mockStream)
|
|
|
+ ;(OpenAI as any).prototype.chat = {
|
|
|
+ completions: { create: mockCreate },
|
|
|
+ } as any
|
|
|
+
|
|
|
+ const generator = handler.createMessage("test", [])
|
|
|
+ await expect(generator.next()).rejects.toThrow("OpenRouter API Error 429: Rate limit exceeded")
|
|
|
+
|
|
|
+ // Verify telemetry was NOT captured for 429 errors
|
|
|
+ expect(mockCaptureException).not.toHaveBeenCalled()
|
|
|
})
|
|
|
|
|
|
it("yields tool_call_end events when finish_reason is tool_calls", async () => {
|
|
|
@@ -384,7 +441,7 @@ describe("OpenRouterHandler", () => {
|
|
|
)
|
|
|
})
|
|
|
|
|
|
- it("handles API errors", async () => {
|
|
|
+ it("handles API errors and captures telemetry", async () => {
|
|
|
const handler = new OpenRouterHandler(mockOptions)
|
|
|
const mockError = {
|
|
|
error: {
|
|
|
@@ -399,9 +456,17 @@ describe("OpenRouterHandler", () => {
|
|
|
} as any
|
|
|
|
|
|
await expect(handler.completePrompt("test prompt")).rejects.toThrow("OpenRouter API Error 500: API Error")
|
|
|
+
|
|
|
+ // Verify telemetry was captured
|
|
|
+ expect(mockCaptureException).toHaveBeenCalledWith(expect.any(ApiProviderError), {
|
|
|
+ provider: "OpenRouter",
|
|
|
+ modelId: mockOptions.openRouterModelId,
|
|
|
+ operation: "completePrompt",
|
|
|
+ errorCode: 500,
|
|
|
+ })
|
|
|
})
|
|
|
|
|
|
- it("handles unexpected errors", async () => {
|
|
|
+ it("handles unexpected errors and captures telemetry", async () => {
|
|
|
const handler = new OpenRouterHandler(mockOptions)
|
|
|
const mockCreate = vitest.fn().mockRejectedValue(new Error("Unexpected error"))
|
|
|
;(OpenAI as any).prototype.chat = {
|
|
|
@@ -409,6 +474,35 @@ describe("OpenRouterHandler", () => {
|
|
|
} as any
|
|
|
|
|
|
await expect(handler.completePrompt("test prompt")).rejects.toThrow("Unexpected error")
|
|
|
+
|
|
|
+ // Verify telemetry was captured
|
|
|
+ expect(mockCaptureException).toHaveBeenCalledWith(expect.any(ApiProviderError), {
|
|
|
+ provider: "OpenRouter",
|
|
|
+ modelId: mockOptions.openRouterModelId,
|
|
|
+ operation: "completePrompt",
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ it("does NOT capture telemetry for 429 rate limit errors", async () => {
|
|
|
+ const handler = new OpenRouterHandler(mockOptions)
|
|
|
+ const mockError = {
|
|
|
+ error: {
|
|
|
+ message: "Rate limit exceeded",
|
|
|
+ code: 429,
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ const mockCreate = vitest.fn().mockResolvedValue(mockError)
|
|
|
+ ;(OpenAI as any).prototype.chat = {
|
|
|
+ completions: { create: mockCreate },
|
|
|
+ } as any
|
|
|
+
|
|
|
+ await expect(handler.completePrompt("test prompt")).rejects.toThrow(
|
|
|
+ "OpenRouter API Error 429: Rate limit exceeded",
|
|
|
+ )
|
|
|
+
|
|
|
+ // Verify telemetry was NOT captured for 429 errors
|
|
|
+ expect(mockCaptureException).not.toHaveBeenCalled()
|
|
|
})
|
|
|
})
|
|
|
})
|