@@ -8,6 +8,17 @@ import { PreviousConfigSnapshot } from "../interfaces/config"
88// Mock ContextProxy
99vi . mock ( "../../../core/config/ContextProxy" )
1010
11+ // Mock embeddingModels module
12+ vi . mock ( "../../../shared/embeddingModels" )
13+
14+ // Import mocked functions
15+ import { getDefaultModelId , getModelDimension , getModelScoreThreshold } from "../../../shared/embeddingModels"
16+
17+ // Type the mocked functions
18+ const mockedGetDefaultModelId = vi . mocked ( getDefaultModelId )
19+ const mockedGetModelDimension = vi . mocked ( getModelDimension )
20+ const mockedGetModelScoreThreshold = vi . mocked ( getModelScoreThreshold )
21+
1122describe ( "CodeIndexConfigManager" , ( ) => {
1223 let mockContextProxy : any
1324 let configManager : CodeIndexConfigManager
@@ -339,6 +350,14 @@ describe("CodeIndexConfigManager", () => {
339350 } )
340351
341352 it ( "should NOT require restart when models have same dimensions" , async ( ) => {
353+ // Mock both models to have same dimension
354+ mockedGetModelDimension . mockImplementation ( ( provider , modelId ) => {
355+ if ( modelId === "text-embedding-3-small" || modelId === "text-embedding-ada-002" ) {
356+ return 1536
357+ }
358+ return undefined
359+ } )
360+
342361 // Initial state with text-embedding-3-small (1536D)
343362 mockContextProxy . getGlobalState . mockReturnValue ( {
344363 codebaseIndexEnabled : true ,
@@ -794,6 +813,14 @@ describe("CodeIndexConfigManager", () => {
794813 } )
795814
796815 it ( "should fall back to model-specific threshold when user setting is undefined" , async ( ) => {
816+ // Mock the model score threshold
817+ mockedGetModelScoreThreshold . mockImplementation ( ( provider , modelId ) => {
818+ if ( provider === "ollama" && modelId === "nomic-embed-code" ) {
819+ return 0.15
820+ }
821+ return undefined
822+ } )
823+
797824 mockContextProxy . getGlobalState . mockReturnValue ( {
798825 codebaseIndexEnabled : true ,
799826 codebaseIndexQdrantUrl : "http://qdrant.local" ,
@@ -840,6 +867,14 @@ describe("CodeIndexConfigManager", () => {
840867 } )
841868
842869 it ( "should use model-specific threshold with openai-compatible provider" , async ( ) => {
870+ // Mock the model score threshold
871+ mockedGetModelScoreThreshold . mockImplementation ( ( provider , modelId ) => {
872+ if ( provider === "openai-compatible" && modelId === "nomic-embed-code" ) {
873+ return 0.15
874+ }
875+ return undefined
876+ } )
877+
843878 mockContextProxy . getGlobalState . mockImplementation ( ( key : string ) => {
844879 if ( key === "codebaseIndexConfig" ) {
845880 return {
@@ -882,6 +917,14 @@ describe("CodeIndexConfigManager", () => {
882917 } )
883918
884919 it ( "should handle priority correctly: user > model > default" , async ( ) => {
920+ // Mock the model score threshold
921+ mockedGetModelScoreThreshold . mockImplementation ( ( provider , modelId ) => {
922+ if ( provider === "ollama" && modelId === "nomic-embed-code" ) {
923+ return 0.15
924+ }
925+ return undefined
926+ } )
927+
885928 // Test 1: User setting takes precedence
886929 mockContextProxy . getGlobalState . mockReturnValue ( {
887930 codebaseIndexEnabled : true ,
@@ -1501,6 +1544,13 @@ describe("CodeIndexConfigManager", () => {
15011544 } )
15021545
15031546 describe ( "loadConfiguration" , ( ) => {
1547+ beforeEach ( ( ) => {
1548+ // Set default mock behaviors
1549+ mockedGetDefaultModelId . mockReturnValue ( "text-embedding-3-small" )
1550+ mockedGetModelDimension . mockReturnValue ( undefined )
1551+ mockedGetModelScoreThreshold . mockReturnValue ( undefined )
1552+ } )
1553+
15041554 it ( "should load configuration and return proper structure" , async ( ) => {
15051555 const mockConfigValues = {
15061556 codebaseIndexEnabled : true ,
@@ -1634,5 +1684,131 @@ describe("CodeIndexConfigManager", () => {
16341684 configManager = new CodeIndexConfigManager ( mockContextProxy )
16351685 expect ( configManager . isConfigured ( ) ) . toBe ( false )
16361686 } )
1687+
1688+ describe ( "currentModelDimension" , ( ) => {
1689+ beforeEach ( ( ) => {
1690+ vi . clearAllMocks ( )
1691+ } )
1692+
1693+ it ( "should return model's built-in dimension when available" , async ( ) => {
1694+ // Mock getModelDimension to return a built-in dimension
1695+ mockedGetModelDimension . mockReturnValue ( 1536 )
1696+
1697+ mockContextProxy . getGlobalState . mockReturnValue ( {
1698+ codebaseIndexEnabled : true ,
1699+ codebaseIndexEmbedderProvider : "openai" ,
1700+ codebaseIndexEmbedderModelId : "text-embedding-3-small" ,
1701+ codebaseIndexEmbedderModelDimension : 2048 , // Custom dimension should be ignored
1702+ codebaseIndexQdrantUrl : "http://localhost:6333" ,
1703+ } )
1704+ mockContextProxy . getSecret . mockImplementation ( ( key : string ) => {
1705+ if ( key === "codeIndexOpenAiKey" ) return "test-key"
1706+ return undefined
1707+ } )
1708+
1709+ configManager = new CodeIndexConfigManager ( mockContextProxy )
1710+ await configManager . loadConfiguration ( )
1711+
1712+ // Should return model's built-in dimension, not custom
1713+ expect ( configManager . currentModelDimension ) . toBe ( 1536 )
1714+ expect ( mockedGetModelDimension ) . toHaveBeenCalledWith ( "openai" , "text-embedding-3-small" )
1715+ } )
1716+
1717+ it ( "should use custom dimension only when model has no built-in dimension" , async ( ) => {
1718+ // Mock getModelDimension to return undefined (no built-in dimension)
1719+ mockedGetModelDimension . mockReturnValue ( undefined )
1720+
1721+ mockContextProxy . getGlobalState . mockReturnValue ( {
1722+ codebaseIndexEnabled : true ,
1723+ codebaseIndexEmbedderProvider : "openai-compatible" ,
1724+ codebaseIndexEmbedderModelId : "custom-model" ,
1725+ codebaseIndexEmbedderModelDimension : 2048 , // Custom dimension should be used
1726+ codebaseIndexQdrantUrl : "http://localhost:6333" ,
1727+ } )
1728+ mockContextProxy . getSecret . mockImplementation ( ( key : string ) => {
1729+ if ( key === "codebaseIndexOpenAiCompatibleApiKey" ) return "test-key"
1730+ return undefined
1731+ } )
1732+
1733+ configManager = new CodeIndexConfigManager ( mockContextProxy )
1734+ await configManager . loadConfiguration ( )
1735+
1736+ // Should use custom dimension as fallback
1737+ expect ( configManager . currentModelDimension ) . toBe ( 2048 )
1738+ expect ( mockedGetModelDimension ) . toHaveBeenCalledWith ( "openai-compatible" , "custom-model" )
1739+ } )
1740+
1741+ it ( "should return undefined when neither model dimension nor custom dimension is available" , async ( ) => {
1742+ // Mock getModelDimension to return undefined
1743+ mockedGetModelDimension . mockReturnValue ( undefined )
1744+
1745+ mockContextProxy . getGlobalState . mockReturnValue ( {
1746+ codebaseIndexEnabled : true ,
1747+ codebaseIndexEmbedderProvider : "openai-compatible" ,
1748+ codebaseIndexEmbedderModelId : "unknown-model" ,
1749+ // No custom dimension set
1750+ codebaseIndexQdrantUrl : "http://localhost:6333" ,
1751+ } )
1752+ mockContextProxy . getSecret . mockImplementation ( ( key : string ) => {
1753+ if ( key === "codebaseIndexOpenAiCompatibleApiKey" ) return "test-key"
1754+ return undefined
1755+ } )
1756+
1757+ configManager = new CodeIndexConfigManager ( mockContextProxy )
1758+ await configManager . loadConfiguration ( )
1759+
1760+ // Should return undefined
1761+ expect ( configManager . currentModelDimension ) . toBe ( undefined )
1762+ expect ( mockedGetModelDimension ) . toHaveBeenCalledWith ( "openai-compatible" , "unknown-model" )
1763+ } )
1764+
1765+ it ( "should use default model ID when modelId is not specified" , async ( ) => {
1766+ // Mock getDefaultModelId and getModelDimension
1767+ mockedGetDefaultModelId . mockReturnValue ( "text-embedding-3-small" )
1768+ mockedGetModelDimension . mockReturnValue ( 1536 )
1769+
1770+ mockContextProxy . getGlobalState . mockReturnValue ( {
1771+ codebaseIndexEnabled : true ,
1772+ codebaseIndexEmbedderProvider : "openai" ,
1773+ // No modelId specified
1774+ codebaseIndexQdrantUrl : "http://localhost:6333" ,
1775+ } )
1776+ mockContextProxy . getSecret . mockImplementation ( ( key : string ) => {
1777+ if ( key === "codeIndexOpenAiKey" ) return "test-key"
1778+ return undefined
1779+ } )
1780+
1781+ configManager = new CodeIndexConfigManager ( mockContextProxy )
1782+ await configManager . loadConfiguration ( )
1783+
1784+ // Should use default model ID
1785+ expect ( configManager . currentModelDimension ) . toBe ( 1536 )
1786+ expect ( mockedGetDefaultModelId ) . toHaveBeenCalledWith ( "openai" )
1787+ expect ( mockedGetModelDimension ) . toHaveBeenCalledWith ( "openai" , "text-embedding-3-small" )
1788+ } )
1789+
1790+ it ( "should ignore invalid custom dimension (0 or negative)" , async ( ) => {
1791+ // Mock getModelDimension to return undefined
1792+ mockedGetModelDimension . mockReturnValue ( undefined )
1793+
1794+ mockContextProxy . getGlobalState . mockReturnValue ( {
1795+ codebaseIndexEnabled : true ,
1796+ codebaseIndexEmbedderProvider : "openai-compatible" ,
1797+ codebaseIndexEmbedderModelId : "custom-model" ,
1798+ codebaseIndexEmbedderModelDimension : 0 , // Invalid dimension
1799+ codebaseIndexQdrantUrl : "http://localhost:6333" ,
1800+ } )
1801+ mockContextProxy . getSecret . mockImplementation ( ( key : string ) => {
1802+ if ( key === "codebaseIndexOpenAiCompatibleApiKey" ) return "test-key"
1803+ return undefined
1804+ } )
1805+
1806+ configManager = new CodeIndexConfigManager ( mockContextProxy )
1807+ await configManager . loadConfiguration ( )
1808+
1809+ // Should return undefined since custom dimension is invalid
1810+ expect ( configManager . currentModelDimension ) . toBe ( undefined )
1811+ } )
1812+ } )
16371813 } )
16381814} )
0 commit comments