@@ -1500,3 +1500,100 @@ func TestBuildConfigFromClaims(t *testing.T) {
15001500 require .Equal (t , 8123 , cfg .Port )
15011501 })
15021502}
1503+
1504+ func TestLazyLoading_OpenAPISchema (t * testing.T ) {
1505+ ctx := context .Background ()
1506+ chConfig := setupClickHouseContainer (t )
1507+
1508+ // Create view
1509+ client , err := clickhouse .NewClient (ctx , * chConfig )
1510+ require .NoError (t , err )
1511+ defer func () { require .NoError (t , client .Close ()) }()
1512+ _ , _ = client .ExecuteQuery (ctx , "DROP VIEW IF EXISTS default.v_lazy" )
1513+ _ , err = client .ExecuteQuery (ctx , "CREATE VIEW default.v_lazy AS SELECT * FROM default.test WHERE id={id:UInt64}" )
1514+ require .NoError (t , err )
1515+
1516+ // Server config
1517+ cfg := config.Config {
1518+ ClickHouse : * chConfig ,
1519+ Server : config.ServerConfig {
1520+ JWE : config.JWEConfig {Enabled : false },
1521+ DynamicTools : []config.DynamicToolRule {
1522+ {Regexp : "default\\ .v_lazy" , Prefix : "lazy_" },
1523+ },
1524+ },
1525+ }
1526+
1527+ // Initialize server (dynamic tools not loaded yet)
1528+ s := NewClickHouseMCPServer (cfg , "test" )
1529+
1530+ // Verify dynamic tools map is empty initially
1531+ s .dynamicToolsMu .RLock ()
1532+ require .Empty (t , s .dynamicTools )
1533+ s .dynamicToolsMu .RUnlock ()
1534+
1535+ // Call ServeOpenAPISchema
1536+ rr := httptest .NewRecorder ()
1537+ req := httptest .NewRequest ("GET" , "/openapi" , nil )
1538+
1539+ s .ServeOpenAPISchema (rr , req )
1540+ require .Equal (t , http .StatusOK , rr .Code )
1541+
1542+ // Verify dynamic tools map is populated
1543+ s .dynamicToolsMu .RLock ()
1544+ require .Len (t , s .dynamicTools , 1 )
1545+ _ , ok := s .dynamicTools ["lazy_default_v_lazy" ]
1546+ require .True (t , ok )
1547+ s .dynamicToolsMu .RUnlock ()
1548+
1549+ // Verify schema contains the path
1550+ var schema map [string ]interface {}
1551+ require .NoError (t , json .Unmarshal (rr .Body .Bytes (), & schema ))
1552+ paths := schema ["paths" ].(map [string ]interface {})
1553+ // Path format: /{jwe_token}/openapi/{tool}
1554+ _ , ok = paths ["/{jwe_token}/openapi/lazy_default_v_lazy" ]
1555+ require .True (t , ok )
1556+ }
1557+
1558+ func TestLazyLoading_MCPTools (t * testing.T ) {
1559+ ctx := context .Background ()
1560+ chConfig := setupClickHouseContainer (t )
1561+
1562+ // Create view
1563+ client , err := clickhouse .NewClient (ctx , * chConfig )
1564+ require .NoError (t , err )
1565+ defer func () { require .NoError (t , client .Close ()) }()
1566+ _ , _ = client .ExecuteQuery (ctx , "DROP VIEW IF EXISTS default.v_mcp_lazy" )
1567+ _ , err = client .ExecuteQuery (ctx , "CREATE VIEW default.v_mcp_lazy AS SELECT * FROM default.test WHERE id={id:UInt64}" )
1568+ require .NoError (t , err )
1569+
1570+ // Server config
1571+ cfg := config.Config {
1572+ ClickHouse : * chConfig ,
1573+ Server : config.ServerConfig {
1574+ JWE : config.JWEConfig {Enabled : false },
1575+ DynamicTools : []config.DynamicToolRule {
1576+ {Regexp : "default\\ .v_mcp_lazy" , Prefix : "mcp_" },
1577+ },
1578+ },
1579+ }
1580+
1581+ // Initialize server
1582+ s := NewClickHouseMCPServer (cfg , "test" )
1583+
1584+ // Verify dynamic tools map is empty initially
1585+ s .dynamicToolsMu .RLock ()
1586+ require .Empty (t , s .dynamicTools )
1587+ s .dynamicToolsMu .RUnlock ()
1588+
1589+ // Simulate middleware calling EnsureDynamicTools
1590+ err = s .EnsureDynamicTools (ctx )
1591+ require .NoError (t , err )
1592+
1593+ // Verify tool is registered in dynamic tools map
1594+ s .dynamicToolsMu .RLock ()
1595+ require .Len (t , s .dynamicTools , 1 )
1596+ _ , ok := s .dynamicTools ["mcp_default_v_mcp_lazy" ]
1597+ require .True (t , ok )
1598+ s .dynamicToolsMu .RUnlock ()
1599+ }
0 commit comments