@@ -584,4 +584,189 @@ describe("CodeIndexServiceFactory", () => {
584584 expect ( ( ) => factory . createVectorStore ( ) ) . toThrow ( "Qdrant URL missing for vector store creation" )
585585 } )
586586 } )
587+
588+ describe ( "validateEmbedderConfig" , ( ) => {
589+ beforeEach ( ( ) => {
590+ vitest . clearAllMocks ( )
591+ // Mock the static validation methods
592+ MockedOpenAiEmbedder . validateEndpoint = vitest . fn ( ) . mockResolvedValue ( true )
593+ MockedCodeIndexOllamaEmbedder . validateEndpoint = vitest . fn ( ) . mockResolvedValue ( true )
594+ MockedOpenAICompatibleEmbedder . validateEndpoint = vitest . fn ( ) . mockResolvedValue ( true )
595+ } )
596+
597+ it ( "should validate OpenAI configuration with provided config" , async ( ) => {
598+ // Arrange
599+ const providedConfig = {
600+ embedderProvider : "openai" ,
601+ modelId : "text-embedding-3-large" ,
602+ openAiOptions : {
603+ openAiNativeApiKey : "test-api-key" ,
604+ } ,
605+ }
606+
607+ // Act
608+ const result = await factory . validateEmbedderConfig ( providedConfig )
609+
610+ // Assert
611+ expect ( result ) . toBe ( true )
612+ expect ( MockedOpenAiEmbedder . validateEndpoint ) . toHaveBeenCalledWith ( "test-api-key" , "text-embedding-3-large" )
613+ } )
614+
615+ it ( "should validate Ollama configuration with provided config" , async ( ) => {
616+ // Arrange
617+ const providedConfig = {
618+ embedderProvider : "ollama" ,
619+ modelId : "nomic-embed-text:latest" ,
620+ ollamaOptions : {
621+ ollamaBaseUrl : "http://localhost:11434" ,
622+ } ,
623+ }
624+
625+ // Act
626+ const result = await factory . validateEmbedderConfig ( providedConfig )
627+
628+ // Assert
629+ expect ( result ) . toBe ( true )
630+ expect ( MockedCodeIndexOllamaEmbedder . validateEndpoint ) . toHaveBeenCalledWith (
631+ "http://localhost:11434" ,
632+ "nomic-embed-text:latest" ,
633+ )
634+ } )
635+
636+ it ( "should validate OpenAI-compatible configuration with provided config" , async ( ) => {
637+ // Arrange
638+ const providedConfig = {
639+ embedderProvider : "openai-compatible" ,
640+ modelId : "custom-model" ,
641+ openAiCompatibleOptions : {
642+ baseUrl : "https://api.example.com/v1" ,
643+ apiKey : "test-api-key" ,
644+ } ,
645+ }
646+
647+ // Act
648+ const result = await factory . validateEmbedderConfig ( providedConfig )
649+
650+ // Assert
651+ expect ( result ) . toBe ( true )
652+ expect ( MockedOpenAICompatibleEmbedder . validateEndpoint ) . toHaveBeenCalledWith (
653+ "https://api.example.com/v1" ,
654+ "test-api-key" ,
655+ "custom-model" ,
656+ )
657+ } )
658+
659+ it ( "should use current config when no config is provided" , async ( ) => {
660+ // Arrange
661+ const currentConfig = {
662+ embedderProvider : "openai" ,
663+ modelId : "text-embedding-3-small" ,
664+ openAiOptions : {
665+ openAiNativeApiKey : "current-api-key" ,
666+ } ,
667+ }
668+ mockConfigManager . getConfig . mockReturnValue ( currentConfig as any )
669+
670+ // Act
671+ const result = await factory . validateEmbedderConfig ( )
672+
673+ // Assert
674+ expect ( result ) . toBe ( true )
675+ expect ( mockConfigManager . getConfig ) . toHaveBeenCalled ( )
676+ expect ( MockedOpenAiEmbedder . validateEndpoint ) . toHaveBeenCalledWith (
677+ "current-api-key" ,
678+ "text-embedding-3-small" ,
679+ )
680+ } )
681+
682+ it ( "should throw error for missing OpenAI API key" , async ( ) => {
683+ // Arrange
684+ const providedConfig = {
685+ embedderProvider : "openai" ,
686+ modelId : "text-embedding-3-large" ,
687+ openAiOptions : {
688+ openAiNativeApiKey : undefined ,
689+ } ,
690+ }
691+
692+ // Act & Assert
693+ await expect ( factory . validateEmbedderConfig ( providedConfig ) ) . rejects . toThrow ( "OpenAI API key is required" )
694+ } )
695+
696+ it ( "should throw error for missing Ollama base URL" , async ( ) => {
697+ // Arrange
698+ const providedConfig = {
699+ embedderProvider : "ollama" ,
700+ modelId : "nomic-embed-text:latest" ,
701+ ollamaOptions : {
702+ ollamaBaseUrl : undefined ,
703+ } ,
704+ }
705+
706+ // Act & Assert
707+ await expect ( factory . validateEmbedderConfig ( providedConfig ) ) . rejects . toThrow ( "Ollama base URL is required" )
708+ } )
709+
710+ it ( "should throw error for missing OpenAI-compatible credentials" , async ( ) => {
711+ // Arrange
712+ const providedConfig = {
713+ embedderProvider : "openai-compatible" ,
714+ modelId : "custom-model" ,
715+ openAiCompatibleOptions : {
716+ baseUrl : undefined ,
717+ apiKey : "test-api-key" ,
718+ } ,
719+ }
720+
721+ // Act & Assert
722+ await expect ( factory . validateEmbedderConfig ( providedConfig ) ) . rejects . toThrow (
723+ "OpenAI-compatible base URL and API key are required" ,
724+ )
725+ } )
726+
727+ it ( "should throw error for invalid embedder type" , async ( ) => {
728+ // Arrange
729+ const providedConfig = {
730+ embedderProvider : "invalid-provider" ,
731+ modelId : "some-model" ,
732+ }
733+
734+ // Act & Assert
735+ await expect ( factory . validateEmbedderConfig ( providedConfig ) ) . rejects . toThrow (
736+ "Invalid embedder type: invalid-provider" ,
737+ )
738+ } )
739+
740+ it ( "should propagate validation errors from embedder" , async ( ) => {
741+ // Arrange
742+ const providedConfig = {
743+ embedderProvider : "openai" ,
744+ modelId : "text-embedding-3-large" ,
745+ openAiOptions : {
746+ openAiNativeApiKey : "invalid-key" ,
747+ } ,
748+ }
749+ MockedOpenAiEmbedder . validateEndpoint = vitest . fn ( ) . mockRejectedValue ( new Error ( "Invalid API key" ) )
750+
751+ // Act & Assert
752+ await expect ( factory . validateEmbedderConfig ( providedConfig ) ) . rejects . toThrow ( "Invalid API key" )
753+ } )
754+
755+ it ( "should use default model ID when not provided" , async ( ) => {
756+ // Arrange
757+ const providedConfig = {
758+ embedderProvider : "openai" ,
759+ openAiOptions : {
760+ openAiNativeApiKey : "test-api-key" ,
761+ } ,
762+ }
763+
764+ // Act
765+ const result = await factory . validateEmbedderConfig ( providedConfig )
766+
767+ // Assert
768+ expect ( result ) . toBe ( true )
769+ expect ( MockedOpenAiEmbedder . validateEndpoint ) . toHaveBeenCalledWith ( "test-api-key" , undefined )
770+ } )
771+ } )
587772} )
0 commit comments