1+ // Hoist mock functions so they can be used in vi.mock factories
2+ const { mockCreate, mockComplete, mockCaptureException } = vi . hoisted ( ( ) => ( {
3+ mockCreate : vi . fn ( ) ,
4+ mockComplete : vi . fn ( ) ,
5+ mockCaptureException : vi . fn ( ) ,
6+ } ) )
7+
18// Mock Mistral client - must come before other imports
2- const mockCreate = vi . fn ( )
3- const mockComplete = vi . fn ( )
49vi . mock ( "@mistralai/mistralai" , ( ) => {
510 return {
611 Mistral : vi . fn ( ) . mockImplementation ( ( ) => ( {
@@ -38,8 +43,18 @@ vi.mock("@mistralai/mistralai", () => {
3843 }
3944} )
4045
46+ // Mock TelemetryService
47+ vi . mock ( "@roo-code/telemetry" , ( ) => ( {
48+ TelemetryService : {
49+ instance : {
50+ captureException : mockCaptureException ,
51+ } ,
52+ } ,
53+ } ) )
54+
4155import type { Anthropic } from "@anthropic-ai/sdk"
4256import type OpenAI from "openai"
57+ import { ApiProviderError } from "@roo-code/types"
4358import { MistralHandler } from "../mistral"
4459import type { ApiHandlerOptions } from "../../../shared/api"
4560import type { ApiHandlerCreateMessageMetadata } from "../../index"
@@ -59,6 +74,7 @@ describe("MistralHandler", () => {
5974 handler = new MistralHandler ( mockOptions )
6075 mockCreate . mockClear ( )
6176 mockComplete . mockClear ( )
77+ mockCaptureException . mockClear ( )
6278 } )
6379
6480 describe ( "constructor" , ( ) => {
@@ -135,7 +151,24 @@ describe("MistralHandler", () => {
135151
136152 it ( "should handle errors gracefully" , async ( ) => {
137153 mockCreate . mockRejectedValueOnce ( new Error ( "API Error" ) )
138- await expect ( handler . createMessage ( systemPrompt , messages ) . next ( ) ) . rejects . toThrow ( "API Error" )
154+ await expect ( handler . createMessage ( systemPrompt , messages ) . next ( ) ) . rejects . toThrow (
155+ "Mistral completion error: API Error" ,
156+ )
157+ } )
158+
159+ it ( "should capture telemetry exception on createMessage error" , async ( ) => {
160+ mockCreate . mockRejectedValueOnce ( new Error ( "API Error" ) )
161+
162+ await expect ( handler . createMessage ( systemPrompt , messages ) . next ( ) ) . rejects . toThrow ( )
163+
164+ expect ( mockCaptureException ) . toHaveBeenCalledTimes ( 1 )
165+ expect ( mockCaptureException ) . toHaveBeenCalledWith ( expect . any ( ApiProviderError ) )
166+
167+ const capturedError = mockCaptureException . mock . calls [ 0 ] [ 0 ] as ApiProviderError
168+ expect ( capturedError . message ) . toBe ( "API Error" )
169+ expect ( capturedError . provider ) . toBe ( "Mistral" )
170+ expect ( capturedError . modelId ) . toBe ( "codestral-latest" )
171+ expect ( capturedError . operation ) . toBe ( "createMessage" )
139172 } )
140173
141174 it ( "should handle thinking content as reasoning chunks" , async ( ) => {
@@ -483,5 +516,20 @@ describe("MistralHandler", () => {
483516 mockComplete . mockRejectedValueOnce ( new Error ( "API Error" ) )
484517 await expect ( handler . completePrompt ( "Test prompt" ) ) . rejects . toThrow ( "Mistral completion error: API Error" )
485518 } )
519+
520+ it ( "should capture telemetry exception on completePrompt error" , async ( ) => {
521+ mockComplete . mockRejectedValueOnce ( new Error ( "API Error" ) )
522+
523+ await expect ( handler . completePrompt ( "Test prompt" ) ) . rejects . toThrow ( )
524+
525+ expect ( mockCaptureException ) . toHaveBeenCalledTimes ( 1 )
526+ expect ( mockCaptureException ) . toHaveBeenCalledWith ( expect . any ( ApiProviderError ) )
527+
528+ const capturedError = mockCaptureException . mock . calls [ 0 ] [ 0 ] as ApiProviderError
529+ expect ( capturedError . message ) . toBe ( "API Error" )
530+ expect ( capturedError . provider ) . toBe ( "Mistral" )
531+ expect ( capturedError . modelId ) . toBe ( "codestral-latest" )
532+ expect ( capturedError . operation ) . toBe ( "completePrompt" )
533+ } )
486534 } )
487535} )
0 commit comments